Introduction
Hello everyone, this is my first post and in it I want to share a cool thing.
This is not a guide or manual, (I never knew how to write them) this is a concept of an idea that I liked and it will be cool if it inspires someone =)
So ... I love wine. You probably know how easy it is to get lost in the variety of varieties, blends, producing countries with a lot of regions inside each, year of harvest and so on, the taste of the drink depends on all these and many other parameters. When I like wine, I I make notes about him, so as not to forget.
It would be cool if these notes were always at hand, how to do it? Of course, write an application =)
In addition to wine, I also love the django and django-rest-framework with their help, and we will implement the idea.
Task
The application is a system of notes, where each user can create, modify and delete those of which is the author. We do not know yet whether it will be a web site or a mobile client. The database provider does not play a role because it is only a concept (I I chose postgres) As an additional functionality, registration through social networks is provided and sharing of your notes through them is the same. In principle, everything is simple.
Implementation
full code available on github repo:
https://github.com/sixth-fox/vinecode
my requirements.txt:
Django==2.2.3
djangorestframework==3.10.1
psycopg2==2.8.3
python-decouple==3.1
pytz==2019.1
sqlparse==0.3.0
By the way I am used to writing code from model to routers, i know that some hold the opposite opinion.
I decided to make the structure like a tasting note, I had to go through a lot of options and in the end I stopped at
class Note(models.Model):
PERFECTLY = 5
GOOD = 4
SATISFACTORILY = 3
BAD = 2
ABOMINATION = 1
SCORE_CHOICES = (
(5, 'Excellent'),
(4, 'Good'),
(3, 'Satisfactory'),
(2, 'Bad'),
(1, 'Vinegar'),
)
date_create = models.DateTimeField(default=timezone.now)
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE, related_name='notes')
slug = models.SlugField(max_length=150, unique_for_date='date_create', default='')
date = models.DateField()
wine = models.CharField(max_length=150)
region = models.CharField(max_length=150)
variety = models.CharField(max_length=150)
country = models.CharField(max_length=150)
vintage = models.DateField()
alcohol = models.FloatField()
price = models.PositiveSmallIntegerField()
appearance = models.CharField(max_length=150)
aromas = models.CharField(max_length=150)
flavors = models.CharField(max_length=150)
score = models.SmallIntegerField(choices=SCORE_CHOICES)
As you can see, I have a basic user model, as long as this is enough.
Since we have nested models, override the create and update methods in the serializer.
from django.contrib.auth.models import User
from rest_framework import serializers
from .models import Note
class UserSerializer(serializers.HyperlinkedModelSerializer):
notes = serializers.HyperlinkedRelatedField(many=True,
view_name='notes:note-detail',
read_only=True)
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User(
username=validated_data.get('username', None)
)
user.set_password(validated_data.get('password', None))
user.save()
return user
def update(self, instance, validated_data):
for field in validated_data:
if field == 'password':
instance.set_password(validated_data.get(field))
else:
instance.__setattr__(field, validated_data.get(field))
instance.save()
return instance
class Meta:
model = User
fields = ('id', 'username', 'email', 'notes', 'password')
extra_kwargs = {
'url': {
'view_name': 'notes:user-detail',
}
}
class NoteSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Note
exclude = ('date_create',)
extra_kwargs = {
'url': {
'view_name': 'notes:note-detail',
}
}
The views is implemented on the inheritance of generics, the most controversial point is that I did not use routers, it is certainly not quite pythonic, sorry
notes/views.py
@api_view(['GET'])
def root_api(request, format=None):
return Response({
'users': reverse('notes:user-list', request=request, format=format),
'notes': reverse('notes:note-list', request=request, format=format),
})
class NoteList(generics.ListCreateAPIView):
queryset = Note.objects.all()
serializer_class = NoteSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
#more methods on github
Add custom permissions in permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method == permissions.SAFE_METHODS:
return True
return obj.owner == request.user
finally urls.py
notes/urls.py
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views
app_name = 'notes'
urlpatterns = [
path('', views.root_api, name='api-root'),
path('note_list/', views.NoteList.as_view(), name='note-list'),
path('note_detail/<int:pk>/', views.NoteDetail.as_view(), name='note-detail'),
path('user_list', views.UserList.as_view(), name='user-list'),
path('user_detail/<int:pk>/', views.UserDetail.as_view(), name='user-detail'),
]
urlpatterns = format_suffix_patterns(urlpatterns)
Top comments (2)
hi man i just want to ask
how did you publish the python code like that in the post :D
Hi, sorry for not responding so long (I sometimes think for a long time)
Use three gravis(unicode U+0300) (your language) (your code) three gravis(unicode U+0300)
I hope I helped=D