Not getting value of other attributes using select_related in django - python

I am trying to fetch the values from TypingTest model. The foreign key is referencing to the UserProfile model. While fetching the data I want the username to be displayed in the result of the query as well. So, after researching a bit I found out that select_related could be a way of doing this. But when I am trying to use select_related. I am not able to see the username, Only the value of the primary key is getting shown.
These are the two models that I have.
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
userName = models.CharField(max_length=26,unique=True)
email = models.EmailField(max_length=254,unique=True)
profilePhoto = models.ImageField(default="/default.png")
rank = models.CharField(max_length=26,default="Novice")
def __str__(self):
return self.userName
class TypingTest(models.Model):
user = models.ForeignKey(UserProfile,on_delete=models.CASCADE)
time = models.IntegerField()
wpm = models.IntegerField()
accuracy = models.IntegerField()
raw = models.IntegerField()
dateTaken = models.DateTimeField(auto_now_add=True)
Correspoding serializer class:
class TypingTestSerializer(serializers.ModelSerializer):
class Meta :
model = TypingTest
fields = ('id','user','time','wpm','accuracy','raw','dateTaken')
class UserSerializer(serializers.ModelSerializer):
class Meta :
model = UserProfile
fields = ('userName','email','profilePhoto','rank')
This is the view code that I have written.
#protected_resource()
#api_view(['GET'])
def getAllTests (request):
testListQuerySet = models.TypingTest.objects.all()
selectRelated = models.TypingTest.objects.select_related('user').only("userName")
print(selectRelated.values())
serializer=serializers.TypingTestSerializer(models.TypingTest.objects.select_related('user'),many=True)
return Response(serializer.data)
The output that I am getting is :
<QuerySet [{'id': 1, 'user_id': 3, 'time': 60, 'wpm': 60, 'accuracy': 91, 'raw': 79, 'dateTaken': datetime.datetime(2023, 2, 14, 21, 7, 32, 721899, tzinfo=datetime.timezone.utc)}, {'id': 2, 'user_id': 4, 'time': 30, 'wpm': 82, 'accuracy': 99, 'raw': 85, 'dateTaken': datetime.datetime(2023, 2, 14, 21, 33, 45, 326814, tzinfo=datetime.timezone.utc)}]>
Please let me know if this is the right way of doing the thing intended and if not what is?

To include the userName field in the output, you should use the UserSerializer to serialize the related UserProfile object instead. Currently, you should use the source argument to specify the related field name in the TypingTest model, and the UserSerializer to serialize the related object so:
class TypingTestSerializer(serializers.ModelSerializer):
userName = serializers.CharField(source='user.userName')
class Meta:
model = TypingTest
fields = ('id','user','userName','time','wpm','accuracy','raw','dateTaken')
Then in view, remove the select_related method call as it is not necessary so:
#protected_resource()
#api_view(['GET'])
def getAllTests (request):
testListQuerySet = models.TypingTest.objects.all()
serializer = serializers.TypingTestSerializer(testListQuerySet, many=True)
return Response(serializer.data)

if you want to get a username of all objects then you have to query like this
TypingTest.objects.select_related('user').values('userName')

select_related is just to fetch related in the same query and preventing to make a new DB hit for accessing FK. Read about it here.
In order to get user data, you need no get it, and select_related cause no more DB hit. You can get it by this way for example:
user = models.TypingTest.objects.select_related('user').first().user
And if you want to represent it in your response, you have to add user to your seriliazer fields:
class TypingTestSerializer(serializers.ModelSerializer):
class Meta:
model = TypingTest
fields = ("user", ...)

Related

Django filter returns queryset with ID instead of username

Hi guys so I have a search function but when using the .objects.filter() method I get a queryset that shows an ID instead of the username.
This is the view:
def search_expense(request):
if request.method == 'POST':
search_str = json.loads(request.body).get('searchText')
expenses = Expense.objects.filter(
amount__istartswith=search_str) | Expense.objects.filter(
date__icontains=search_str) | Expense.objects.filter(
description__icontains=search_str) | Expense.objects.filter(
category__icontains=search_str)
data = expenses.values()
return JsonResponse(list(data), safe=False)
<QuerySet [{'id': 16, 'amount': 2.33, 'date': datetime.date(2020, 10, 2), 'description': 'Something', 'owner_id': 1, 'category': 'Food'}]>
So instead of the 'owner_id': 1 I need it to be 'owner': username
The model (the User model is Django's standard model):
class Expense(models.Model):
amount = models.FloatField()
date = models.DateField(default=now)
description = models.TextField()
owner = models.ForeignKey(to=User, on_delete=models.CASCADE)
category = models.CharField(max_length=255)
def __str__(self):
return self.category
class Meta:
ordering: ['-date']
You can add new fields to .values().
from django.db.models import F
fields = tuple(x.name for x in Expense._meta.get_fields())
data = expenses.values(*fields, owner_name=F("owner__value"))
You will also have to specify all the fields you want manually, or if you want all the fields then you can just use .get_fields() like I have done.
You have to name the new field something other than "owner", or you will get a ValueError:
ValueError: The annotation 'owner' conflicts with a field on the model.
Showing the ID of ForeignKey fields is the intended behaviour of .values() according to the documentation:
If you have a field called foo that is a ForeignKey, the default values() call will return a dictionary key called foo_id, since this is the name of the hidden model attribute that stores the actual value (the foo attribute refers to the related model).

Getting date value from django queryset

I have a Models like below
class Member(models.Model):
memberid = models.AutoField(primary_key=True, editable=False)
memberdate = models.DateTimeField(default=timezone.now)
fname = models.CharField(max_length=25)
mname = models.CharField(max_length=25)
lname = models.CharField(max_length=25)
mobile1 = models.CharField(max_length=15)
email = models.CharField(max_length=150)
dob = models.DateTimeField(default=timezone.now)
I am fetching data to display in the html template. For the view below is the code
def updateMemberView(request, id):
searchmem= id
member = Member.objects.filter(memberid=searchmem).values()
print(member[0])
return render(request, 'Member/member_update_form.html', {"member": member})
Now in print(member[0]) I am getting
{'memberid': 13, 'memberdate': datetime.datetime(2020, 4, 11, 0, 0, tzinfo=<UTC>), 'fname': 'Akash', 'mname': 'chimanbhai', 'lname': 'khatri', 'mobile1': '', 'email': 'kashkhatri#yahoo.com', 'dob': datetime.datetime(2020, 4, 3, 0, 0, tzinfo=<UTC>)}
But when I try to print the value of dob in template using member.0.dob it gives me error.
Also when I try to execute command
print(member[0].dob)
this also give me error
'dict' object has no attribute 'dob'
So How could I get the dob value in view and also in template.
This is a dictionary. You access the value corresponding to a key in a dictionary by subscripting:
print(member[0]['dob'])
That being said, using .values(..) [Django-doc] should be used scarcely, especially since you erase the logical layer of the model. For example foreign keys are no longer retrieved lazily, you only have the value of the primary key.
You can simply pass the model object to the template:
from django.shortcuts import get_object_or_404
def update_member_view(request, id):
member = get_object_or_404(Member, memberid=id)
return render(request, 'Member/member_update_form.html', {'member': member})
Some common mistakes:
functions are normally written in slug_case, not in PerlCase, or camelCase;
it is better to use get_object_or_404 to return a HTTP 404 exception if the object can not be found; and
as said before, pass the Member object itself, that way you can add extra methods to the model that you can use in the tempate.

Django rest framework nested serialization not working properly

I have these serializers in my app:
class ScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = Schedule
fields = ('id',)
class DisciplineSerializer(serializers.ModelSerializer):
class Meta:
model = Discipline
fields = ('id',)
class WriteTeacherSerializer(serializers.ModelSerializer):
disciplines = DisciplineSerializer(many=True)
schedules = ScheduleSerializer(many=True)
class Meta:
model = Teacher
fields = ('phone_number', 'bio', 'price', 'disciplines', 'schedules')
depth = 1
def update(self, instance, validated_data):
print "\n"
#Debugging here
print validated_data
print "\n"
print instance.__dict__
print "\n"
instance.phone_number = validated_data['phone_number']
instance.bio = validated_data['bio']
instance.price = validated_data['price']
disciplines = validated_data.pop('disciplines')
schedules = validated_data.pop('schedules')
for discipline in disciplines:
try:
stored_discipline = Discipline.objects.get(id=discipline['id'])
instance.disciplines.add(stored_discipline)
except Discipline.DoesNotExist:
raise Http404
for schedule in schedules:
try:
stored_schedule = Schedule.objects.get(id=schedule['id'])
instance.schedules.add(stored_discipline)
except Discipline.DoesNotExist:
raise Http404
instance.save()
return instance
As you can see I am trying a nested serialization with the fields schedules and disciplines. I think I followed the documentation, but the nested serialization is not working when I test it. I printed the instance and validated_data objects and tested it on the shell.
I start the data in this format:
data = {u'phone_number': u'+99999999999', u'bio': u'BIO', u'price': 40, u'disciplines': [{'id': 1}], u'schedules': [{'id': 2}]}
I got a teacher instance and started the serializer like this:
serializer = WriteTeacherSerializer(teacher, data=data)
It shows True on a serializer.is_valid() call.
However when I try to save it the validated_data and the instance.__dict__ are like that:
#validated_data
{u'phone_number': u'+5584998727770', u'bio': u'BIO', u'price': 40, u'disciplines': [OrderedDict()], u'schedules': [OrderedDict()]}
#instance.__dict__
{'phone_number': u'', 'bio': u'', 'price': 50, 'profile_id': 2, '_state': <django.db.models.base.ModelState object at 0xb64a6bec>, 'id': 6}
They don't seem to notice the nested fields wich makes the update() method not work.
Am I doing something wrong?
Here is my Teacher Model as well:
class Teacher(models.Model):
price = models.IntegerField(default=50)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Wrong phone number format.")
phone_number = models.CharField(validators=[phone_regex], max_length=15, blank=True)
profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
schedules = models.ManyToManyField(Schedule, related_name='schedules')
disciplines = models.ManyToManyField(Discipline, related_name='disciplines')
bio = models.CharField(max_length=200, blank=True)
If you are just sending IDs then you don't need to add the nested serializer, just specify the field name of the ForeignKey or ManyToManyField.
class WriteTeacherSerializer(serializers.ModelSerializer):
class Meta:
model = Teacher
fields = ('phone_number', 'bio', 'price', 'disciplines', 'schedules')
I am also wondering if it is because you have a depth=1 flag?
DRF doesn't support nested updates out of the box. You have to override the Serializer's update method, and write your own update logic, so you'd be seeing an error for this warning if you were sending nested data.

Django REST Framework DateTimeField format showing Python Time

Given some model
class Loan(models.Model):
time_of_loan = models.DateTimeField()
username = models.CharField()
I have attempted to use the ModelSerializer from Django's REST Framework to serialize the Loan.
class LoanSerializer(serializers.ModelSerializer):
time_of_loan = serializers.DateTimeField(
format=None, input_formats=['%Y-%m-%d %H:%M:%S',])
class Meta:
model = `Loan`
fields = ['time_of_loan', 'username']
On using the serializer.data to get the JSON format, when I save the first time, the first time the model is saved, the JSON is well-behaved.
{
'time_of_loan': '2016-06-20 00:00:00+08:00',
'username': 'doe'
}
However, when I attempt to update the model, it "misbehaves" and it appears in a python datetime format.
{
'time_of_loan': datetime.datetime(2016, 6, 20, 7, 55, tzinfo=<UTC>),
'username': 'doe'
}
What change do I need to do so that, whenever the model gets serialized, it remains as the first format that I want?
FIRST EDIT
Can you show what you're doing to update the object
The question asked was what I did to update the model. I actually am using this as an audit log and so it took from an actual Django Form. In forms.py:
id = forms.cleaned_data.get('id')
username = forms.cleaned_data.get('username')
loan = Loan.objects.filter(id=id) #Queryset with count() = 1
loan.update(username=username)
loan_obj = loan[0]
serializer = LoanSerializer(loan_obj)
print(serializer.data)
After so much finding, I finally got the answer.
from rest_framework.renderers import JSONRenderer
serializer = LoanSerializer(loan_obj)
serializer.data
json = JSONRenderer().render(serializer.data)

How to retrieve object attributes of an OneToOneField relation in Django-Rest-Framework

I'm trying to extend the User model with a OneToOneField, so I can add more fields to a user:
class Userattribs(models.Model):
user = models.OneToOneField(User)
passcode = models.IntegerField(default=0)
# about user
organisation = models.CharField(max_length=100, null=True)
description = models.CharField(max_length=300, null=True)
I also have the following model serializers:
class UserattribsSerializer(serializers.ModelSerializer):
class Meta:
model = Userattribs
fields = ('organisation', 'description')
class UserSerializer(serializers.ModelSerializer):
userattribs = UserattribsSerializer(required=True)
class Meta:
model = User
fields =('id', 'username', 'first_name', 'last_name', 'email', 'userattribs')
The problem I'm having is the serialisation of a User, does not include the 'userattribs' in the json response. I've spent hours googling and banging my head. I'd be eternally grateful if you help me out.
Thanks in advance!
I've added the views. One odd thing I noticed was that when I queried (using UserList) all data, I would get the Userattribs. However, when I retrieve a single item, I don't get the Userattribs
###################################################
### Views #########################################
###################################################
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (permissions.AllowAny,)
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (access_permission,)
I don't see any error in your code. Are you sure that your User objects have an Userattribs object.
Open your python shell with python manage.py shell and enter this to test your code :
#import your related models and serializer
from your_app.models import *
from your_app.serializer import *
# create new user
user = User.objects.create(username="test")
# create new user attrib linked to `user` object
user_attrib = Userattribs.objects.create(user=user, organisation="DJANGO")
# serialize your `user` object
user_serializer = UserSerializer(user)
# display your serialized data
user_serializer.data
# outputs : {'last_name': '', 'userattribs': OrderedDict([('organisation', 'DJANGO'), ('description', None)]), 'email': '', 'username': 'test', 'first_name': '', 'id': 3}
If it ok, the problem maybe come from your view.

Categories