are django querysets fastest? - performance

models.py
class Comments(UUID):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
post = models.ForeignKey(Posts, on_delete=models.CASCADE)
comment = models.ForeignKey(
"self", on_delete=models.CASCADE, blank=True, null=True
)
text = models.TextField()
files = models.ImageField()
serializers.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comments
fields = "__all__"
extra_kwargs = {
"user": {"read_only": True},
"text": {"required": False},
"files": {"required": False}
}
def create(self, validated_data):
user = self.context["request"].user
return Comments.objects.create(user=user, **validated_data)
views.py
class PostCommentsRetrieveAPIView(generics.RetrieveAPIView):
serializer_class = CommentSerializer
def get(self, request, *args, **kwargs):
replies_count = Subquery(Comments.objects.filter(
comment=OuterRef("pk")
).values("comment").annotate(count=Count("pk")).values("count"))
comment_remarks = Subquery(Remarks.objects.filter(
comment=OuterRef("pk")
).values("comment").annotate(count=Count("pk")).annotate(
popularities=popularities
).values("popularities"))
# https://stackoverflow.com/questions/63020407/return-multiple-values-in-subquery-in-django-orm
replies = Subquery(Comments.objects.filter(
comment=OuterRef("pk")
).values("comment", "created_at", "updated_at").annotate(
created=Now() - F("created_at"), created_=created_,
updated=Now() - F("updated_at"), updated_=updated_
).annotate(
details=ArrayAgg(
JSONObject(
id="id",
user_id="user_id",
username="user__profile__username",
text="text",
files="files",
created="created_",
updated="updated_",
profile_link=profile_link,
profile_image=profile_picture,
comment_remarks=comment_remarks,
replies_count=replies_count
)
)
).values("details"))
comment = Subquery(Comments.objects.filter(
post=OuterRef("pk"), comment=None
).values("post", "created_at", "updated_at").annotate(
created=Now() - F("created_at"), created_=created_,
updated=Now() - F("updated_at"), updated_=updated_
).values("created_").annotate(
details=ArrayAgg(
JSONObject(
id="id",
user_id="user_id",
username="user__profile__username",
text="text",
files="files",
created="created_",
updated="updated_",
profile_link=profile_link,
profile_image=profile_picture,
comment_remarks=comment_remarks,
comment_replies=replies
)
)
).values("details"))
post = Posts.objects.filter(id=kwargs["post_id"]).annotate(
comment=comment,
).values("comment")
return Response(post, status=status.HTTP_200_OK)
Now I just want to ask is that a best way show comments on post with nested replies, I create another files to calculate datetime, profile link, profile image, remarks on comment
this query is hitting database only one time my question is will it be fastest query to get data from database?
how to can I check that my query is fastest or not

Related

IntegrityError at /api/order/create NOT NULL constraint failed: store_order.product_id

I'm trying to create Order via Api request with related keys, product and user id's. But getting an error that they cannot be null, although they're not null.
model.py
class Order(models.Model):
id = models.AutoField(primary_key=True)
product = models.ForeignKey(Product, related_name='product', on_delete=models.CASCADE, default = None)
user = models.ForeignKey(User, related_name='user', on_delete=models.CASCADE, default = None)
orderPrice = models.IntegerField(default=0)
status = models.CharField(max_length=255, default='Принят на обработку')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
serializer.py
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['id', 'product', 'user', 'status', 'orderPrice']
depth = 1
view.py
#api_view(['POST'])
def OrderCreateView(request):
order_serializer = OrderSerializer(data=request.data)
if order_serializer.is_valid(raise_exception=True):
order_serializer.save()
return JsonResponse(order_serializer.data, safe=False)
and finally my api request
{
"product": 1,
"user": 3,
"status": "Принят на обработку",
"orderPrice": 30000
}
This code worked sometime but i did couple changes and now it getting an error. I tried to remigrate all my models, and requested like that "user_id", "product_id". But keeps getting the error
Hi
the problem of your code is your serializer because you mention a depth of 1 and you send a JSON in your POST functiona JSON in coherent.
Your json should contain all the attributes of your foreign keys like this:
{
"product": {
...attributes of product
},
"user": {
...attributes of user
},
"status": "Принят на обработку",
"orderPrice": 30000
}
You just have to declare the product field as nullable, because null is False by delfault:
class Order(models.Model):
[...]
product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True, blank=True)
[...]
I also removed related_name, as to retrieve the order of a product, you had to call product.product. Now you retrieve it using product.order.

How to customize dj-rest-auth Login serializer

I am using dj-rest-auth, django-allauth and djangorestframework-simplejwt.I want to customize dj-rest-auth Login serializer to include the following additional fields: (first_name, last_name, phone_number), validate them, authenticate them and login properly. Also i am using email instead of username as my main identifier.
This is my Custom User Model:
from django.db import models
from django.contrib.auth.models import AbstractUser, BaseUserManager
from phonenumber_field.modelfields import PhoneNumberField
class CustomUserManager(BaseUserManager):
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError("An email has to be set")
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, **extra_fields)
class CustomUserModel(AbstractUser):
username = None
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
phone_number = PhoneNumberField()
bio = models.TextField(null=True, blank=True)
age = models.PositiveIntegerField(null=True, blank=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = [
"first_name",
"last_name",
"phone_number",
]
def __str__(self):
return self.first_name + " " + self.last_name
objects = CustomUserManager()
This is my serializer.py:
from django.conf import settings
from django.contrib.auth import get_user_model, authenticate
from rest_framework import serializers, exceptions
from dj_rest_auth.registration.serializers import RegisterSerializer
from dj_rest_auth.serializers import LoginSerializer
from allauth.account.adapter import get_adapter
from phonenumber_field.serializerfields import PhoneNumberField
class UserListSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
read_only_fields = [
"id",
"is_superuser",
"first_name",
"last_name",
"phone_number",
"bio",
"age",
"date_joined",
]
fields = [
"id",
"is_superuser",
"first_name",
"last_name",
"phone_number",
"bio",
"age",
"date_joined",
]
class UserDetailSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = [
"id",
"is_superuser",
"email",
"password",
"first_name",
"last_name",
"phone_number",
"date_joined",
"last_login",
"bio",
"age",
]
read_only_fields = [
"is_superuser",
"password",
]
class CustomRegisterSerializer(RegisterSerializer):
username = None
first_name = serializers.CharField(max_length=50)
last_name = serializers.CharField(max_length=50)
phone_number = PhoneNumberField()
class Meta:
model = get_user_model()
fields = [
"email",
"first_name",
"last_name",
"phone_number",
"password",
]
def get_cleaned_data(self):
return {
"email": self.validated_data.get("email", ""),
"first_name": self.validated_data.get("first_name", ""),
"last_name": self.validated_data.get("last_name", ""),
"phone_number": self.validated_data.get("phone_number", ""),
"password1": self.validated_data.get("password1", ""),
"password2": self.validated_data.get("password2", ""),
}
def save(self, request):
adapter = get_adapter()
user = adapter.new_user(request)
self.cleaned_data = self.get_cleaned_data()
user.first_name = self.cleaned_data.get("first_name")
user.last_name = self.cleaned_data.get("last_name")
user.phone_number = self.cleaned_data.get("phone_number")
user.save()
adapter.save_user(request, user, self)
return user
UserModel = get_user_model()
class CustomLoginSerializer(LoginSerializer):
username = None
first_name = serializers.CharField()
last_name = serializers.CharField()
phone_number = PhoneNumberField()
def authenticate(self, **options):
return authenticate(self.context["request"], **options)
def validate(self, attrs):
email = attrs.get("email")
first_name = attrs.get("first_name")
last_name = attrs.get("last_name")
phone_number = attrs.get("phone_number")
password = attrs.get("password")
if email and first_name and last_name and phone_number and password:
user = authenticate(
email=email,
first_name=first_name,
last_name=last_name,
phone_number=phone_number,
pasword=password,
)
if not user:
msg = "Incorrect credentials."
raise serializers.ValidationError(msg, code="authorization")
else:
msg = "No email provided."
raise exceptions.ValidationError(msg)
attrs["user"] = user
return attrs
This is my views.py:
from django.contrib.auth import get_user_model
from rest_framework import generics
from .serializers import UserListSerializer, UserDetailSerializer
class UserListView(generics.ListCreateAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserListSerializer
class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserDetailSerializer
I have all the settings setup correctly, so i have no problems there. My custom registration work fine too, eveything works fine. I just can't correctly override the Loginserializer. And for my endpoint url i am using dj-rest-auth 's default "dj-rest-auth/registration/" address.
I hope you were able to find the right way to do it. I also had the same issue with LoginSerializer until I've added this to settings.py
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
This is my serializers.py
class AccountLoginSerializer(LoginSerializer):
username = None
def authenticate(self, **options):
return authenticate(self.context["request"], **options)
def validate(self, attrs):
email = attrs.get("email")
ip_address = get_client_ip(self.context["request"])[0]
password = attrs.get("password")
if email and password:
'''Check if IP address belongs to account with given email address'''
try:
if Account.objects.get(email=email).ip_address.filter(ip_address=ip_address).exists():
user = authenticate(
email=email,
password=password,
)
else:
msg = "IP address doesn't match. An email has been sent to verify this IP address."
raise serializers.ValidationError(msg, code="authorization")
if not user:
msg = "Invalid credentials."
raise serializers.ValidationError(msg, code="authorization")
except ObjectDoesNotExist:
msg = "Invalid credentials."
raise serializers.ValidationError(msg, code="authorization")
else:
msg = "No email provided."
raise exceptions.ValidationError(msg)
attrs["user"] = user
return attrs
Hope it helps someone with the same issue.

How to get multiple records in the main model based on an array from a nested serializer?

I have next code:
models.py
class tbox(models.Model):
name = models.CharField("box", max_length=15)
status = models.CharField("status box", max_length=50)
class tbox_move(models.Model):
tbox = models.ForeignKey('api.tbox',related_name='tbox_moves', on_delete=models.DO_NOTHING)
discription = models.TextField(null=True, blank=True)
def __str__(self):
return '%d: %s' % (self.order, self.title)
serializer.py
from rest_framework import serializers
from .models import tbox, tbox_move
class tboxSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False, write_only=False)
class Meta:
model = tbox
fields = ['id', 'name', 'status']
def update(self, instance, validated_data):
print("Updade in serializer TBOX")
class tbox_moveSerializer(serializers.ModelSerializer):
tbox_moves = tboxSerializer(many=True)
class Meta:
model = tbox_move
fields = ['tbox_moves', 'discription']
def create(self, validated_data):
tboxs_data = validated_data.pop('tbox_moves')
for tbox_data in tboxs_data:
tbox_id = tbox_data.get("id", None)
if tbox_id is None:
print("requare BOX ID")
id_tbox = tbox.objects.filter(id=tbox_id).update(**tbox_data)
tbox_m = tbox_move.objects.create(tbox=tbox_id, **validated_data)
return tbox_m
I send next request POST json:
{
"tbox_moves" : [
{"id" : 3, "id_test" : 1, "name" : "name_box1", "status": "block"},
{"id" : 1, "id_test" : 1, "name" : "name_box2", "status": "block"}
],
"description": "collective data"
}
I want to get the following result.
Create multiple records in the model equal to the number in the nested serializer
Example tbox_move model:
ID
discription
tbox_id
10
collective data
3
11
collective data
1
I want update status in the nested model tbox from enable to block
How this create?

Nested write serializer validated_date is empty

Followed the best practive create nested objects with serializers, however I still receive empty nested validate_data.
Serializers:
class WriteOrganisationSiteSerializer(serializers.ModelSerializer):
"""Organisation Site serializer class for post methods."""
site = WriteAPSiteSerializer()
class Meta:
model = models.OrganisationSite
fields = ("organisation", "site")
def create(self, validated_data):
from fadat.sites.models import APSite
site_data = validated_data.pop("site")
activeplaces_site_id = site_data.pop("activeplaces_site_id")
site, created = APSite.objects.get_or_create(
activeplaces_site_id=activeplaces_site_id, defaults=site_data
)
organisation_site = models.OrganisationSite.objects.create(
site=site, **validated_data
)
return organisation_site
class WriteAPSiteSerializer(serializers.Serializer):
"""Active Places Site serializer class for post methods."""
class Meta:
model = models.APSite
fields = (
"activeplaces_site_id",
"site_name",
"dependent_thoroughfare",
"thoroughfare_name",
"double_dependent_locality",
"dependent_locality",
"post_town",
"postcode",
"easting",
"northing",
"longitude",
"latitude",
)
The view
class OrganisationSitesView(APIView):
"""Organisation Sites API view."""
def post(self, request, **kwargs):
user = request.user
ser = serializers.WriteOrganisationSiteSerializer(data=request.data)
ser.is_valid(raise_exception=True)
obj = ser.save()
ser = serializers.ReadOrganisationSiteSerializer(obj)
return Response(ser.data, status=201)
Running the following test (or via browser a ajax request, same result)
def test_add_organisation_site(self):
user = User.objects.create(email="newbie#dat.example.com")
organisation_type = OrganisationType.objects.create(name="Club")
organisation = Organisation.active_objects.create(
name="Club", organisation_type=organisation_type
)
site = {
"activeplaces_site_id": 1200341,
"site_name": "CITY OF LONDON SCHOOL",
"dependent_thoroughfare": "",
"thoroughfare_name": "QUEEN VICTORIA STREET",
"double_dependent_locality": "",
"dependent_locality": "",
"post_town": "LONDON",
"postcode": "EC4V 3AL",
"easting": 531990,
"northing": 180834,
"longitude": -0.099387,
"latitude": 51.511025,
}
body = {
"organisation": organisation.id,
"site": site,
}
self.authenticate(user)
url = reverse("api:inspections:organisation-sites")
res = self.client.post(url, json.dumps(body), content_type="application/json; charset=utf-8")
self.assertEqual(res.status_code, 201)
Receiving the following headers in my views
{'organisation': 1, 'site': {'activeplaces_site_id': 1200341, 'site_name': 'CITY OF LONDON SCHOOL', 'dependent_thoroughfare': '', 'thoroughfare_name': 'QUEEN VICTORIA STREET', 'double_dependent_locality': '', 'dependent_locality': '', 'post_town': 'LONDON', 'postcode': 'EC4V 3AL', 'easting': 531990, 'northing': 180834, 'longitude': -0.099387, 'latitude': 51.511025}}
In the view the request.data show me
{'Cookie': '', 'Content-Length': '368', 'Content-Type': 'application/json; charset=utf-8', 'Authorization': 'Token 0081d8a36d90f1d922a2a7df494afe127a220495'}
Still the serialized doesn't validate nested fields and returns
{'organisation': <Organisation: Club (Club)>, 'site': OrderedDict()}
Right, here's what I see that needs fixing: the conventional way to design a post()method is as follows:
def post(self, request, **kwargs):
serializer = serializers.WriteOrganisationSiteSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Although your case is in many ways equivalent, it's obviously not 100% correct. Perhaps it has to do with the fact that you're using one serializer to create the new instance, and another to show the result, when that's not needed: you can set up which fields are read_only, which ones are write_only, and those that are both read and write, as the documentation explains. I suggest you follow the schema I laid out above to guide you, and define in WriteOrganisationSiteSerializer how to show the data to the end user. Do let me know if you have any problems.

Django Rest Framework - can't created object with existing FK reference

I have a serializer class in DRF like so:
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
created_by = UserSerializer() # created_by is an FK to User model
class Meta:
model = Project
fields = ('id', 'title', 'created_by')
My views:
class ProjectList(generics.ListCreateAPIView):
model = Project
serializer_class = ProjectSerializer
filter_fields = ('title',)
def post(self, request, format=None):
serializer = ProjectSerializer(data={
"id": 12,
"title": "FooBar's Project",
"created_by": {
"id": 1,
"username": "foobar",
"first_name": "foo",
"last_name": "bar",
},
})
if serializer.is_valid():
serializer.save()
else:
print serializer.errors
return Response(serializer.data, status=status.HTTP_201_CREATED)
This works almost as expected except that DRF complains that:
{'created_by': [{'username': [u'User with this Username already exists.']}]}
What I want is that the Project is created with reference to an existing user. What am I doing wrong here?
Note: I am tracking trunk for DRF, not from the cheeseshop.
If you want to assign an existing user, not creating one, nested serializer is not the way. As you want to just add the relation to the user, you will need to take a look at relations.
The PrimaryKeyRelatedField is way to go and since this field is going to be writable you will also need to pass a queryset attribute.
created_by = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
See the documentation to check the additional options that can be passed to this field:
http://django-rest-framework.org/api-guide/relations#primarykeyrelatedfield
Then your example post could look like this:
def post(self, request, format=None):
user = User.objects.get(pk=8)
serializer = ProjectSerializer(data={
"id": 12,
"title": "FooBar's Project",
"created_by": user.pk,
})
(Posted on behalf of the question author).
This is the working solution:
# serializers.py
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
...
created_by = serializers.PrimaryKeyRelatedField(
queryset=User.objects.all()
)
class Meta:
model = Project
fields = ('id', 'title', 'created_by')
# views.py
class ProjectList(generics.ListCreateAPIView):
model = Project
serializer_class = ProjectSerializer
filter_fields = ('title',)
def post(self, request, format=None):
serializer = ProjectSerializer(data={
"title": request.DATA.get('title'),
"created_by": request.user.pk,
})
if serializer.is_valid():
serializer.save()
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.data, status=status.HTTP_201_CREATED)

Resources