Alter django rest framework field before serialization and deserialization - python

what I'm trying to do, is to convert unix_epoch into python dateTime and vice versa while serialization and desiarilization. What I've done so far:
My model Track:
class Track(models.Model):
uuid = models.UUIDField(null=True)
distance = models.FloatField(null=True)
dateCreated = models.DateTimeField(null=True)
dateClosed = models.DateTimeField(null=True)
def date_created_to_epoch(self):
return time.mktime(self.dateCreated.timetuple())
def date_closed_to_epoch(self):
return time.mktime(self.dateClosed.timetuple())
My model Point:
class Point(models.Model):
uuid = models.UUIDField(null=True)
track_uuid = models.UUIDField(null=True)
dateCreated = models.DateTimeField(null=True);
track = models.ForeignKey(Track, related_name='points')
def date_created_to_epoch(self):
return time.mktime(self.dateCreated.timetuple())
My serializers:
class PointSerializer(serializers.ModelSerializer):
dateCreated = serializers.ReadOnlyField(source='date_created_to_epoch')
class Meta:
model = Point
fields = ('uuid', 'lat', 'lng')
class TrackSerializer(serializers.ModelSerializer):
points = PointSerializer(many=True)
dateCreated = serializers.ReadOnlyField(source='date_created_to_epoch')
dateClosed = serializers.ReadOnlyField(source='date_closed_to_epoch')
class Meta:
model = Track
fields = ('uuid', 'distance', 'dateCreated', 'dateClosed', 'comments', 'type', 'status', 'points')
def create(self, validated_data):
points_data = validated_data.pop('points')
track = Track.objects.create(**validated_data)
for point_data in points_data:
Point.objects.create(track=track, **point_data)
return track
As you see, I've made from database to json convertion with def date_created_to_epoch(self): method. What I need is to implement json(unix time) to database (date time) convertion. I use nested models. I think my methods are incorrect and there is a way to make it better. Please help.

I would use Django Rest Framework's built in DateTime fields instead of the raw .ReadOnlyField. You can make any field readonly by passing read_only=True to the field's constructor.

Related

Resolve UUID to DangoObjectNode in Subquery

I've got a database with an simple Employee model and node in Django. I´m using Graphene to create an API around this that allows a user to retrieve the right data.
class Employee(models.Model):
id = models.UUIDField(primary_key=True, unique=True, )
name = models.CharField(max_length=128)
class EmployeeNode(DjangoObjectType):
class Meta:
model = Employee
fields = "__all__"
interfaces = (graphene.relay.Node, )
Now in addition to this, I have a query that finds a "buddy" for every Employee, which is another Employee (ID) in the database, and a function (details irrelevant here) that finds the correct "buddy" in the database using some not further specified Django query.
class EmployeeNodeWithBuddy(DjangoObjectType):
buddy_id = graphene.UUID()
class Meta:
model = Employee
fields = "__all__"
interfaces = (graphene.relay.Node, )
#classmethod
def get_queryset(cls, queryset, info):
set_with_buddy_annotation = queryset.annotate(
buddy_id=ExpressionWrapper(Subquery(
### Omitting the details of this query ###
).only('id')[:1], output_field=models.UUIDField()
), output_field=models.UUIDField())
)
return set_with_buddy_annotation
This works ok, but what I actually want is not the ID of the buddy, but the actual EmployeeNode. I can´t figure out if there is a good way to annotate/add info to this query to make it return the thing I want. It would look like:
class EmployeeNodeWithBuddy(DjangoObjectType):
buddy_id = graphene.UUID()
buddy = graphene.Field(EmployeeNode) # Field with EmployeeNode instead of just ID!
class Meta:
model = Employee
fields = "__all__"
interfaces = (graphene.relay.Node, )
#classmethod
def get_queryset(cls, queryset, info):
set_with_buddy_annotation = queryset.annotate(
buddy_id=ExpressionWrapper(Subquery(
### Omitting the details of this query ###
).only('id')[:1], output_field=models.UUIDField()
), output_field=models.UUIDField())
)
set_with_buddy_annotation = # Somehow fetch the EmployeeNode from the buddy_id here?
return set_with_buddy_annotation
Does this make sense to do and is this even possible?

Can django prefetch related work with 3 lookups?

I want to prefetch 3 tables plus the initial table. Here are model examples of the project i am working on
class ExampleOne(models.Model):
name = models.Charfield()
option = models.BooleanField()
money = MoneyField()
that_date = models.DateTimeField()
class ExampleTwo(models.Model):
word = models.Charfield()
example_one = models.ManyToManyField(ExampleOne)
number = models.IntegerField()
class ExampleThree(models.Model):
number = models.IntegerField()
example_two = models.ForeignKey(ExampleTwo)
this_date = models.DateTimeField()
#property
def get_calculation_one(self):
if self.example_two.example_one.exists():
for example1 in self.example_two.example_one.all():
if example1.option is not None:
if self.this_date >= example1.that_date):
return self.number * example1.money.amount
else:
if self.this_date >= datetime.today()):
return self.number * example1.money.amount
else:
return 0
class ExampleFour(models.Model):
date = models.DateField()
example_three = models.ManyToManyField(ExampleThree)
#property
def get_calculation_two(self):
cost = 0
if self.example_three.exists():
for example3 in self.example_three.all():
cost += example3.get_calculation_one
return cost
return cost
Now I want to know if it is possible to retrieve data from all these models with as little hits to the database as possible because when I try to retrieve data it takes over one minute to retrieve the data and send it to the frontend
The calculation might be making many database hits in order to get the data and calculate and I think that is why it is taking more than a minute to retrieve the data
my view looks like this
qs = ExampleFour.objects.prefetch_related(
Prefetch('example_three__example_two__example_one')
).filter(...)
Is there a way to use prefetch to make the retrieval faster or is there another suggestion on I can rewrite my code in order to avoid this long retrieval time?
Note I am still new to Django
I am also using DRF so here are how the serializers look
class ExampleOneSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleOne
fields = (
"name",
"option",
"money",
"that_date",
)
class ExampleTwoSerializer(serializers.ModelSerializer):
example_1_value = serializers.SerializerMethodField()
class Meta:
model = ExampleOne
fields = (
"word",
"example_one",
"number",
)
read_only_fields =(
"example_1_value",
)
def get_example_1_value(self, obj):
return ExampleOneSerializer(obj.example_one.latest('that_date')
).data
class ExampleThreeSerializer(serializers.ModelSerializer):
example_2_data = serializers.SerializerMethodField()
get_calculation_one = serializers.ReadOnlyField()
class Meta:
model = ExampleOne
fields = (
"number",
"example_two",
"this_date",
"example_2_data",
"get_calculation_one",
)
read_only_fields =(
"example_2_data",
"get_calculation_one",
)
def get_example_2_data(self, obj):
return ExampleTwoSerializer(obj.example_two).data
class ExampleFourSerializer(serializers.ModelSerializer):
example_3_data = serializers.SerializerMethodField()
get_calculation_two = serializers.ReadOnlyField()
class Meta:
model = ExampleOne
fields = (
"date",
"example_three",
"example_3_data",
"get_calculation_two",
)
read_only_fields =(
"example_3_data",
"get_calculation_two",
)
def get_example_3_data(self, obj):
return ExampleThreeSerializer(obj.example_three.all(),
many=True).data
I wonder if would prefetch work on the serializer side or would it be redundant

How to get model data to appear as a field in another model's response

These are simplified versions of my models (the user model is just an id and name)
class Convo(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='convo_owner')
users = models.ManyToManyField(User, through='Convo_user')
class Convo_user (models.Model):
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
convo = models.ForeignKey(Convo, on_delete=models.CASCADE)
class Comments(models.Model):
name = models.CharField(max_length=255)
content = models.TextField(max_length=1024)
convo = models.ForeignKey(Convo, on_delete=models.CASCADE)
This is my view
class ConvoViewSet(viewsets.ModelViewSet):
serializer_class = serializers.ConvoSerializer
def get_queryset(self):
return None
def list(self, request):
curr_user = request.user.id
# Collecting the list of conversations
conversations = models.Conversation.object.filter(ConvoUser__user_id=request.user.id)
#Getting list of conversation id's
conv_ids = list(conversations.values_list('id', flat=True).order_by('id'))
#Getting list of relevant comments
comments = models.Comments.objects.filter(conversation_id__in=conv_ids)
return Response(self.get_serializer(conversations, many=True).data)
And my current serializer
class ConvoSerializer(serializers.ModelSerializer):
"""A serializer for messaging objects"""
# access = AccessSerializer(many=True)
# model = models.Comments
# fields = ('id', 'name', 'content', 'convo_id')
class Meta:
model = models.Convo
fields = ('id', 'owner_id')
The current response I get is of the form
[
{
"id": 1,
"owner_id": 32
}, ...
]
But I would like to add a comments field that shows all the properties of comments into the response, so basically everything in the second queryset (called comments) and I'm not sure how to go about this at all. (I retrieve the comments in the way I do because I'm trying to minimize the calls to the database). Would I need to create a new view for comments, make its own serializer and then somehow combine them into the serializer for the convo?
The way you've set up your models, you can access the comments of each Convo through Django's ORM by using convo_object.comments_set.all(), so you could set up your ConvoSerializer to access that instance's comments, like this:
class ConvoSerializer(serializers.ModelSerializer):
"""A serializer for messaging objects"""
comments_set = CommentSerializer(many=True)
class Meta:
model = models.Convo
fields = ('id', 'owner_id', 'comments_set')
and then you define your CommentSerializer like:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = models.Comments
fields = ('id', 'name', 'content')
No data appears because my serializers are using the default database, not sure why but a step forward
EDIT:
Django: Database used for prefetch_related is not the same that the parent query Provided me the correct answer, I was able to choose the database with this method because for some reason inner queries use the default DB

Django Rest Framework Custom Serializer Method

I'm trying to create a custom serializer method that counts the number of passed and failed quizzes from my QuizResults model. A failed quiz is under .7 and a passed quiz is .7 or over.
I want to be able to look into the Users QuizResult and count the number of passed quizzes(.7 or over). I would then duplicate the method to count the failed quizzes (under .7).
So far I don't have much idea on how to do so. I want to be able to grab the percent_correct field of the model and do a calculation and add it to a field in the serializer called "quiz_passed".
Here is my QuizResult model:
class QuizResult(models.Model):
quiz = models.ForeignKey(Quiz)
user = models.ForeignKey(User, related_name='quiz_parent')
percent_correct = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(1.0)])
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return 'Quiz Results for : ' + self.quiz.title
Here is my serializer:
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
todo_count = serializers.IntegerField(source='todo_parent.count', read_only=True)
discussion_count = serializers.IntegerField(source='comment_parent.count', read_only=True)
quiz_passed = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('todo_count', 'discussion_count', 'quiz_passed', 'username', )
def get_quiz_passed(self, obj):
return passed
Any help is appreciated.
Edit:
I extended the User model and added a model method like you suggested.
class Profile(User):
def get_quizzes_passed_count(self):
return self.quiz_parent.filter(percent_correct__gte=0.8).count()
I then added your suggestion to my ProfileSerializer.
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
todo_count = serializers.IntegerField(source='todo_parent.count', read_only=True)
discussion_count = serializers.IntegerField(source='comment_parent.count', read_only=True)
num_quizzes_passed = serializers.ReadOnlyField(source="get_quizzes_passed_count")
class Meta:
model = Profile
fields = ('todo_count', 'discussion_count', 'num_quizzes_passed', 'username')
Unfortunately when I add this nothing appears in the framework once these have been added. Any suggestions? Thank you.
You can use a model method on the user model to count that user's number of passed quizzes:
class User(models.model):
# rest of your User attributes
def get_quizzes_passed_count(self):
return self.quiz_parent.filter(percent_correct__gte=0.7).count()
Then add that to your serializer using a DRF ReadOnlyField to serialize that method:
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
todo_count = serializers.IntegerField(
source='todo_parent.count', read_only=True
)
discussion_count = serializers.IntegerField(
source='comment_parent.count', read_only=True
)
quiz_passed = serializers.SerializerMethodField()
num_quizzes_passed = serializers.ReadOnlyField(source="get_quizzes_passed_count")
class Meta:
model = User
fields = ('todo_count', 'discussion_count', 'quiz_passed', 'username', )
def get_quiz_passed(self, obj):
return passed
You can duplicate this for the number of failed quizzes.

Is it possible to save the primary key value of more than one fields of a model into an another model's single 'field' using foreign key relation?

models.py:
import datetime
from django.db import models
from pygments.lexers import get_all_lexers
LEXERS = [item for item in get_all_lexers() if item[1]]
class Classname(models.Model):
class_name = models.CharField(max_length=8)
def __str__(self):
return self.class_name
class Sectionname(models.Model):
class_name = models.ForeignKey(Classname)
section_name = models.CharField(max_length=1, default='A')
def __str__(self):
return self.section_name
class Teachername(models.Model):
field = """ I want to define here a foreign key field(inherited from Sectionname model)which saves the primary key value of row corresponding to two fields (class_name, section_name) above."""
teachname = models.CharField(max_length=50, verbose_name='teacher Name')
def __str__(self):
return self.teachname
class Attendancename(models.Model):
teacher_name = models.ForeignKey(Teachername)
date = models.DateField('Date')
intime = models.TimeField('IN-TIME')
outtime = models.TimeField('OUT-TIME')
def hours_conversion(self):
tdelta = (datetime.datetime.combine(datetime.date.today(),self.outtime) - datetime.datetime.combine(datetime.date.today(),self.intime))
hours, minutes = tdelta.seconds//3600, (tdelta.seconds//60)%60
return '{0}hrs {1}mins'.format(hours, minutes)
def __str__(self):
return "%s" %self.teacher_name
forms.py:
from django import forms
from django.forms import ModelForm
from .models import Classname, Sectionname, Teachername, Attendancename
class ClassnameForm(ModelForm):
class_name = forms.CharField(max_length=8)
class Meta:
model = Classname
fields = ('class_name',)
class SectionnameForm(ModelForm):
class_name = forms.ModelChoiceField(queryset=Classname.objects.all())
class Meta:
model = Sectionname
fields = ('section_name', 'class_name',)
class TeachernameForm(ModelForm):
field = """ Here I also want to do the same thing, I tried to make a form field, which shows value of both 'section_name' and 'class_name' from above model but only saves the value of corresponding row's primary key."""
class Meta:
model = Teachername
fields = ('classname', 'secname', 'teachname',)
class AttendancenameForm(ModelForm):
teacher_name = forms.ModelChoiceField(queryset=Teachername.objects.all())
class Meta:
model = Attendancename
fields = ('teacher_name', 'date', 'intime', 'outtime',)
I'm trying to save the 'pk' value of Sectionname model fields('calss_name', 'section_name') into Terachername model's single 'field', I also want to show the both the values to user using form field 'field', but behined the scenes only primary key values needs to be saved.
Is it possible to do so? If it is then how can I implement it in my app?
Please! provide your suggestions....
Thanks! in advance.....
You cannot store two foreign keys to two different tables in a single models.ForeignKey field, and it really wouldn't make any sense (if the reason is not obvious to you then you should learn more about relational model).
But anyway: since a Sectionname belongs to one single Classname, you don't need anything else than the Sectionname pk to get the related Classname:
class Teachername(models.Model):
sectionname = models.ForeignKey(Sectionname)
teachname = models.CharField(max_length=50, verbose_name='teacher Name')
def __str__(self):
return self.teachname
teacher = Teachername.objects.get(pk=XXX)
print teacher, teacher.sectionname, teacher.sectionname.classname
Or if a teacher is supposed to teach more than one section:
class Teachername(models.Model):
sectionnames = models.ManyToMany(Sectionname)
teachname = models.CharField(max_length=50, verbose_name='teacher Name')
def __str__(self):
return self.teachname
teacher = Teachername.objects.get(pk=XXX)
for sectionname in teacher.sectionnames.all():
print teacher, sectionname.classname

Categories

Resources