How to create/update/view two modal class with one serializer - python

I have two model class. I want to create/update/view both model class with one serializer. I have tried but its not working.
Here is my modal class:
class PackageQuantity(models.Model):
comp_code = models.ForeignKey(
'core.SensorDevice',
on_delete=models.SET_NULL,
null=True
)
quantity = models.IntegerField()
class PackageComponent(models.Model):
pkg_code = models.CharField(max_length=255)
package = models.ForeignKey(
'PackageQuantity',
on_delete= models.CASCADE,
null=True
)
created_at=models.DateTimeField(auto_now_add=True)
updated_at=models.DateTimeField(auto_now=True)
Serializer class
class PackageQuantity(serializers.ModelSerializer):
class Meta:
model = oss.PackageQuantity
fields = ['comp_code', 'quantity',]
class PackageComponentSerializer1(serializers.ModelSerializer):
package = PackageQuantity()
class Meta:
model = oss.PackageComponent
fields = ['pkg_code','package']
Views.py
class PackageComponent1ViewSet(viewsets.ModelViewSet):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAdminUser, permissions.IsAuthenticated,)
serializer_class = serializers.PackageComponentSerializer1
queryset = oss.PackageComponent.objects.all()
Any help will be highly appreciated. Thanks

Related

DRF Add annotated field to nested serializer

I have two serializers that represent comments and their nested comments. I'm provide a queryset to viewset with annotated field likes. But my problem is that field only working in parent serializer. When i add this field to nested serializer i got error
Got AttributeError when attempting to get a value for field likes on serializer CommentChildrenSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Comment instance.
Original exception text was: 'Comment' object has no attribute 'likes'.
Here is some my code. Thanks
Models.py
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
slug = models.SlugField(blank=True)
body = models.TextField()
tags = TaggableManager(blank=True)
pub_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-pub_date']
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE,
related_name='comments')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
text = models.TextField(max_length=500)
pub_date = models.DateTimeField(auto_now=True)
parent = models.ForeignKey('self', blank=True, null=True,
on_delete=models.CASCADE, related_name='children')
class Meta:
ordering = ['-pub_date']
class Vote(models.Model):
comment = models.ForeignKey(Comment, on_delete=models.CASCADE,
related_name='votes')
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
choice = models.BooleanField(null=True)
Serializers.py
class PostRetrieveSerializer(PostSerializer):
comments = CommentSerializer(read_only=True, many=True)
author = AuthorInfoSerializer(serializers.ModelSerializer)
class Meta:
model = Post
fields = ['id', 'author', 'slug', 'title', 'body', 'tags', 'pub_date', 'comments']
class CommentChildrenSerializer(serializers.ModelSerializer):
author = AuthorInfoSerializer(read_only=True)
likes = serializers.IntegerField()
class Meta:
model = Comment
fields = ['author', 'id', 'text', 'pub_date', 'parent', 'likes']
class CommentSerializer(serializers.ModelSerializer):
author = AuthorInfoSerializer(read_only=True)
children = CommentChildrenSerializer(many=True)
likes = serializers.IntegerField()
class Meta:
ordering = ['pub_date']
model = Comment
fields = ['author', 'id', 'text', 'pub_date', 'children', 'likes']
Views.py
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all().prefetch_related(
Prefetch('comments', queryset=Comment.objects.filter(parent__isnull=True)
.annotate(likes=Count('votes__choice'))))
serializer_class = PostSerializer
permission_classes = [IsOwnerOrAdminOrReadOnly]
pagination_class = PostPagination
lookup_field = 'slug'
def get_serializer_class(self):
""""
Attach related comments
when get post detail
"""
if self.action == 'retrieve':
return PostRetrieveSerializer
return self.serializer_class
def perform_create(self, serializer):
serializer.save(author=self.request.user)
maybe you can do something like this, adding the like field in each child comment.
queryset = Post.objects.all()\
.prefetch_related(
Prefetch(
'comments',
queryset=Comment.objects\
.filter(parent__isnull=True)\
.annotate(likes=Count('votes__choice'))\
.prefetch_related(
'children',
queryset=Comments.objects.all()\
.annotate(likes=Count('votes__choice'))
)
)
)
I hope this help you.
Regards!
On your model level, add a custom property like the below.
class Comment(models.Model):
...
class Meta:
ordering = ['-pub_date']
#property
def likes(self):
return self.votes.count()
On your serializer add SerializerMethodField
class CommentChildrenSerializer(serializers.ModelSerializer):
author = AuthorInfoSerializer(read_only=True)
likes = serializers.SerializerMethodField() # Change here
class Meta:
model = Comment
fields = ['author', 'id', 'text', 'pub_date', 'parent', 'likes']
# method for the SerializerMethodField
def get_likes(self, obj):
return obj.likes
Update both Comment related serializers. I believe this approach is simpler than the current implementation.

How build a category model after building legacy model in Django REST Framework

Hello, I have a question about improving legacy models.
The Material model is old model, and i want to make category by using new model 'type'. But i have a little problem with when i use admin site. In admin site, i hope to choose the 'type' first, and upload data .. how can i make better
models
# new model
class MaterialType(BaseModel):
type = models.CharField(choices=MaterialTypeChoice.choices, max_length=50, null=True, blank=True)
def __str__(self):
return self.type
# old model
class Material(models.Model):
type = models.ForeignKey(MaterialType, verbose_name=, null=True, blank=True, on_delete=models.SET_NULL)
name = models.CharField max_length=50, null=True, blank=True)
size = models.CharField(max_length=50, null=True, blank=True)
unit = models.CharField(max_length=5, null=True, blank=True)
price_unit = models.IntegerField(null=True, blank=True)
def __str__(self):
return self.name
serializers
# new
class MaterialTypeListSerializer(serializers.ModelSerializer):
class Meta:
model = MaterialType
fields = ["type"]
# old
class MaterialListSerializer(serializers.ModelSerializer):
class Meta:
model = Material
fields = ["id", "type", "name", "size", "unit", "price_unit"]
views
# new
class MaterialTypeList(ListCreateAPIView):
queryset = MaterialType.objects.all()
serializer_class = MaterialTypeListSerializer
# old
class MaterialList(ListAPIView):
queryset = Material.objects.all()
filter_class = MaterialFilter
serializer_class = MaterialListSerializer
admin
#admin.register(Material)
class MaterialAdmin(ImportExportModelAdmin):
list_display = ["name", "size", "unit", "price_unit"]
list_display_links = ("name",)
list_filter = ("type",)
list_per_page = 10
# list_editable = ('type',)
search_fields = ("name", "size")
resource_class = MaterialResource
#admin.register(MaterialType)
class MaterialTypeAdmin(ImportExportModelAdmin):
list_display = ["type"]
list_filter = ("type",)
list_per_page = 10
# list_editable = ('type',)

Correct way to use URL Patterns, Views & Templates when using the Django Abstract base classes for my models

Are there better approaches to this problem?
This model structure is what I am after for my database.
So I am feeling like I am breaking the DRY rule with my learning project.
Using Django 3.2.9 & Python 3.9.6
I have looked around the internet on how to get this more dynamic in the views, urls & templates but I have not had any luck.
I have the following model structure for my models/contact.py
class BaseContact(AbstractUser):
other_name = models.CharField(max_length=50, null=True, blank=True)
contact_number = models.CharField(max_length=10, null=True, blank=True)
email = models.EmailField(unique=True)
postal_address = models.ForeignKey(location.Address, on_delete=models.CASCADE, null=True, blank=True)
tax_id = models.CharField(max_length=11, null=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name']
class Meta:
default_related_name = 'base'
verbose_name = 'Contact'
verbose_name_plural = 'Contacts'
def get_absolute_url(self):
return reverse("%s:detail-%s" % (str(type(self).__name__.lower()), str(type(self).__name__.lower())),
kwargs={"pk": self.pk})
class Customer(BaseContact):
class Meta:
default_related_name = 'customer'
class Supplier(BaseContact):
class Meta:
default_related_name = 'supplier'
class Employee(BaseContact):
class Meta:
default_related_name = 'employee'
verbose_name = 'Employee'
verbose_name_plural = 'Employees'
So my urls/contact.py is looking grim.
from django.urls import path
from main.views.contact import UpdateEmployeeView, DetailEmployeeView, CreateEmployeeView, DeleteEmployeeView
from main.views.contact import UpdateSupplierView, DetailSupplierView, CreateSupplierView, DeleteSupplierView
from main.views.contact import UpdateCustomerView, DetailCustomerView, CreateCustomerView, DeleteCustomerView
urlpatterns = [
path('employee/create/', CreateEmployeeView.as_view(), name='create-employee'),
path('employee/detail/<int:pk>', DetailEmployeeView.as_view(), name='detail-employee'),
path('employee/update/<int:pk>', UpdateEmployeeView.as_view(), name='update-employee'),
path('employee/delete/<int:pk>', DeleteEmployeeView.as_view(), name='delete-employee'),
path('supplier/create/', CreateSupplierView.as_view(), name='create-supplier'),
path('supplier/detail/<int:pk>', DetailSupplierView.as_view(), name='detail-supplier'),
path('supplier/update/<int:pk>', UpdateSupplierView.as_view(), name='update-supplier'),
path('supplier/delete/<int:pk>', DeleteSupplierView.as_view(), name='delete-supplier'),
path('customer/create/', CreateCustomerView.as_view(), name='create-customer'),
path('customer/detail/<int:pk>', DetailCustomerView.as_view(), name='detail-customer'),
path('customer/update/<int:pk>', UpdateCustomerView.as_view(), name='update-customer'),
path('customer/delete/<int:pk>', DeleteCustomerView.as_view(), name='delete-customer'),
]
not much better in the views/contact.py
class CreateEmployeeView(CreateView):
model = Employee
template_name_suffix = '_create_form'
fields = ['__all__']
class DetailEmployeeView(DetailView):
model = Employee
class UpdateEmployeeView(UpdateView):
model = Employee
form_class = UpdateEmployeeForm
template_name_suffix = '_update_form'
class DeleteEmployeeView(DeleteView):
model = Employee
success_url = reverse_lazy('home')
class CreateSupplierView(CreateView):
model = Supplier
template_name_suffix = '_create_form'
fields = ['__all__']
class DetailSupplierView(DetailView):
model = Supplier
class UpdateSupplierView(UpdateView):
model = Supplier
form_class = UpdateSupplierForm
template_name_suffix = '_update_form'
class DeleteSupplierView(DeleteView):
model = Supplier
success_url = reverse_lazy('home')
class CreateCustomerView(CreateView):
model = Customer
template_name_suffix = '_create_form'
fields = ['__all__']
class DetailCustomerView(DetailView):
model = Customer
class UpdateCustomerView(UpdateView):
model = Customer
form_class = UpdateCustomerForm
template_name_suffix = '_update_form'
class DeleteCustomerView(DeleteView):
model = Customer
success_url = reverse_lazy('home')
templates/main folder

count total object in foreign key - drf

i want to count comments for every single Post
in models.py:
class Post(models.Model):
body = models.TextField(max_length=10000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_by = models.ManyToManyField(User, blank=True, related_name='liked_by')
class Meta:
ordering = ['-date']
class Comment(models.Model):
body = models.TextField(max_length=1000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
class Meta:
ordering = ['-date']
in serializers.py:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
#comments = CommentSerializer()
user = UserSerializers()
total_likes = serializers.SerializerMethodField()
liked_by = SimpleUserSerializer(many=True, read_only=True)
total_comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('body','date','user', 'total_likes', 'liked_by','total_comments')
def get_total_likes(self, instance):
return instance.liked_by.count()
def get_total_comments(self, instance):
return instance.comments.count()
when i run this code, it shows, AttributeError: 'Post' object has no attribute 'comments'.
how do i count comments of a post?
Since you haven't configured the related_name, Django uses the default related_name and hence you should access the reveres FK using comment_set instead of comments
Thus, the get_total_comments(...) method should look like
def get_total_comments(self, instance):
return instance.comment_set.count()
Reference
What is related_name used for in Django?

Unable to save relationship between two objects with Django Rest Framework

I am building a Django/React App to allow users to submit orders that need to go from A to B. The user initially saves the addresses in the database and then he/she selects it in the order form. When they submit I attempt to create a relationship in the database, I'm using Django Rest Framework serializers to create the Order object in the database.
Unfortunately, I'm unable to successfully save the items as I'm not properly linking the addresses to the order. Im getting the following error:
destinationAddress: ["Invalid value."]
originAddress: ["Invalid value."]
Models
class Order(models.Model):
originAddress = models.ForeignKey(Address, related_name="originAddress", null=True, on_delete=models.CASCADE)
destinationAddress = models.ForeignKey(Address, related_name="destinationAddress", null=True, on_delete=models.CASCADE)
packages = models.CharField("Packages", max_length=1024)
class Address(models.Model):
address_code = models.CharField(max_length=250)
contact = models.CharField("Contact", max_length=1024)
phone = models.CharField("Phone", max_length=20)
company = models.CharField("Company", max_length=250)
addressLine1 = models.CharField("Address line 1", max_length=1024)
addressLine2 = models.CharField("Address line 2", max_length=1024, blank=True)
postalCode = models.CharField("Postal Code", max_length=12)
city = models.CharField("City", max_length=1024)
state = models.CharField("State", max_length=250)
Serializers
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = '__all__'
class OrderSerializer(serializers.ModelSerializer):
originAddress = serializers.SlugRelatedField(
queryset=Address.objects.all(),
slug_field='pk'
)
destinationAddress = serializers.SlugRelatedField(
queryset=Address.objects.all(),
slug_field='pk'
)
class Meta:
model = Order
fields = ('id', 'packages', 'destinationAddress', 'originAddress')
ViewSets
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
permission_classes = [
permissions.AllowAny
]
serializer_class = OrderSerializer
class AddressViewSet(viewsets.ModelViewSet):
queryset = Address.objects.all()
permission_classes = [
permissions.AllowAny
]
serializer_class = AddressSerializer
Any ideas? Thanks
Solved it by replacing SlugRelatedField to PrimaryKeyRelatedField
class OrderSerializer(serializers.ModelSerializer):
originAddress = serializers.PrimaryKeyRelatedField(
queryset=Address.objects.all(), allow_null=True, required=False
)
destinationAddress = serializers.PrimaryKeyRelatedField(
queryset=Address.objects.all(), allow_null=True, required=False
)
class Meta:
model = Order
fields = ('id', 'packages', 'destinationAddress', 'originAddress')

Categories