DEV Community

Cover image for Code and Wine
i.piatigorets
i.piatigorets

Posted on

Code and Wine

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)
Enter fullscreen mode Exit fullscreen mode

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',
            }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
lord007tn profile image
Raed Bahri

hi man i just want to ask
how did you publish the python code like that in the post :D

Collapse
 
psychopanda profile image
i.piatigorets

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