how to get reverse relation in django serializers SlugRelatedField - python

I have two models ProductTypeModel and ProductModel, product_type is the foreign key in the product. Then I wrote a ModelSerializer for a product to get all the entries of ProductModel and along with some additional info.
Now I'm unable to get product_sub_type from the ProductTypeModel in ProductSerializer
I have tried SlugRelatedField in serializers, tried to set slug_field=product_sub_type and slug_field=product_type__product_sub_type and slug_field=product_type.product_sub_type
models.py
class ProductType(models.Model):
"""Product type model."""
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False
)
hsn = models.ForeignKey(
HSN,
related_name='product_types',
on_delete=models.SET_NULL,
null=True, blank=True
)
product_type = models.CharField(max_length=255, null=True, blank=True)
product_sub_type = models.CharField(max_length=255, db_index=True)
description = models.CharField(max_length=255, null=True, blank=True)
def __str__(self):
return str(self.product_type)
def get_sub_type(self):
return str(self.product_sub_type)
class Meta:
db_table = 'ProductTypes'
unique_together = ('product_type', 'product_sub_type')
class Product(models.Model):
"""Products model."""
product_id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False
)
product_type = models.ForeignKey(
ProductType,
related_name='related_products',
on_delete=models.SET_NULL,
blank=True, null=True
)
name = models.CharField(max_length=255, db_index=True)
code_name = models.CharField(max_length=255, null=True, blank=True)
href = models.CharField(max_length=500, blank=True, null=True)
full_name = models.CharField(max_length=255, null=True, blank=True, db_index=True)
manufacturer = models.ForeignKey(
Manufacturer,
related_name='manufactured_products',
on_delete=models.SET_NULL,
null=True, blank=True
)
packing = models.CharField(max_length=255, null=True, blank=True)
packing_detail = models.CharField(max_length=255, null=True, blank=True)
mrp = models.DecimalField(
max_digits=8,
decimal_places=2,
null=True, blank=True
)
created_at = models.DateTimeField(
'created at',
db_index=True,
default=timezone.now
)
def __str__(self):
return str(self.full_name)
class Meta:
db_table = 'Products'
unique_together = ('code_name', 'product_type')
serializers.py
class ProductTypeSerializer(serializers.ModelSerializer):
# Serialize/Deserialize ProductType instance.
class Meta:
model = ProductType
fields = (
'id', 'hsn',
'product_type', 'product_sub_type',
'description'
)
read_only_fields = ('id', )
class ProductSerializer(serializers.ModelSerializer):
# Serialize/Deserialize Product instance.
manufacturer = serializers.StringRelatedField()
manufacturer_id = serializers.PrimaryKeyRelatedField(read_only=True)
product_sub_type = serializers.SlugRelatedField(slug_field=????)
class Meta:
model = Product
fields = (
'product_id',
'product_type', 'product_sub_type',
'name', 'code_name',
'manufacturer', 'manufacturer_id',
'full_name',
'packing', 'packing_detail',
'mrp'
)
read_only_fields = (
'product_id', 'created_at',
'product_type', 'manufacturer_id'
)
with slug_field=product_sub_type it returns
ImproperlyConfigured at /products/
Field name product_sub_type is not valid for model Product.
with slug_field=product_type.product_sub_type it returns
AttributeError at /products/
Got AttributeError when attempting to get a value for field product_sub_type on serializer ProductSerializer.The serializer field might be named incorrectly and not match any attribute or key on the Product instance.
I want the serializer to return something like this:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"product_id": "fffcf7ba-5c6d-4190-96d2-cc9125e18e71",
"product_type": "1b3dd955-b67e-4ca3-9561-6d1704ff7c91",
"product_sub_type": "Capsules",
"name": "Enshine Toothpaste",
"code_name": null,
"manufacturer": "Leeford Healthcare Ltd",
"manufacturer_id": 2524,
"full_name": "Enshine Toothpaste",
"packing": null,
"packing_detail": null,
"mrp": null
}
]
}

Providing the attributes slug_field and source works for a read-only SlugRelatedField. To allow writing in this field the queryset attribute must also be provided
class ProductSerializer(serializers.ModelSerializer):
...
product_sub_type = serializers.SlugRelatedField(
slug_field='product_sub_type',
source='product_type',
queryset=ProductType.objects.all()
)
According to documentation slug_field should be a field that uniquely identifies any given instance

Related

Using dal for AutocompleteFilter admin filter

I have a Category model which has a field that is self recursive to indicate parent Category. and I want to have an Autocomplete filter in the admin site where I can filter child categories that belong to the same parent.
How can I used the module AutocompleteFilter from dal_admin_filters.
this is my Model:
class Category(models.Model):
name = models.CharField(max_length=100)
parent = models.ForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="childs"
)
description = models.TextField(null=True, blank=True)
picture = models.ImageField(null=True, blank=True)
is_active = models.BooleanField(default=True)
ordering = models.PositiveIntegerField(default=0)
the model is registred in the admin.py :
class CategoryAdmin(SortableAdminMixin, TranslationAdmin, admin.ModelAdmin):
search_fields = ["name"]
fieldsets = (
(
_("Details"),
{
"fields": (
"name",
"parent",
"description",
"picture",
),
},
),
)
list_display = (
"ordering",
"name",
"parent",
)
readonly_fields = ("display_picture",)
admin.site.register(Category, CategoryAdmin)

How to join multiple Model in Django Rest Framework serializer

In Django, I have the following models.
class Property(models.Model):
address1 = models.CharField(max_length=512)
address2 = models.CharField(max_length=128, blank=True, null=True)
property_type = models.ForeignKey('PropertyType', models.DO_NOTHING, null=True, blank=True, default=None)
geo_info = models.ForeignKey(GeoInfo, models.DO_NOTHING)
class Meta:
indexes = [
models.Index(fields=['address1', ]),
]
db_table = 'property'
class PropertyType(models.Model):
name = models.CharField(max_length=128, blank=True, null=True)
category = models.CharField(max_length=45, blank=True, null=True)
color = models.CharField(max_length=45, blank=True, null=True)
class Meta:
db_table = 'property_type'
indexes = [
models.Index(fields=['name', ]),
]
class GeoInfo(models.Model):
zipcode = models.CharField(max_length=25, blank=True, null=True)
city = models.CharField(max_length=45, blank=True, null=True)
state = models.CharField(max_length=45, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True, null=True)
updated_at = models.DateTimeField(auto_now=True, null=True)
class Meta:
db_table = 'geo_info'
indexes = [
models.Index(fields=['zipcode', ]),
models.Index(fields=['city', ]),
]
def __str__(self):
return '%s %s' % (self.zipcode, self.city)
I'm trying to use Django REST Framework to serialize PROPERTY model as follows.
class GeoSerializer(serializers.ModelSerializer):
class Meta:
model = GeoInfo
fields = ['zipcode', 'city', 'state']
class PropertyTypeSerializer(serializers.ModelSerializer):
class Meta:
model = PropertyType
class PropertySerializer(serializers.ModelSerializer):
address_geo = GeoSerializer(many=False, read_only=True)
prop_type = PropertyTypeSerializer(many=True, read_only=True)
class Meta:
model = Property
fields = ['id', 'address1', 'address_geo', 'prop_type']
My expected outcome for each item is like
{
"id": 1,
"address1": "test address",
"geo_info":{
"zipcode":"12121",
"city":"12121",
"state":"12121",
},
"property_type": "unknown",
}
and currently, I'm just getting the property data like
{
"id": 1,
"address1": "test address"
}
So can you help me figure out how to serialize these models?
Also, it would be great if you can help me how to flatten the outcome and avoid nested node if possible
your question is not clear fully. You asked for no nesting. This may help you -
class PropertySerializer(serializers.ModelSerializer):
zipcode=serializers.SerializerMethodField()
city=serializers.SerializerMethodField()
state=serializers.SerializerMethodField()
class Meta:
model = Property
fields = ('id','address1','zipcode','city','state','property_type')
def get_zipcode(self,instance):
return instance.geo_info.zipcode
def get_city(self,instance):
return instance.geo_info.city
def get_state(self,instance):
return instance.geo_info.state

How to join models in django serializers?

I'm trying to join two models, but I got the wrong result. How to do it right?
My models:
class MoocherPage(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=False)
name = models.CharField(max_length=48, unique=True, blank=False, null=False)
bio = models.TextField(blank=False, null=False)
class MoocherResource(models.Model):
url = models.URLField(blank=False, null=False)
moocher = models.ForeignKey(MoocherPage, on_delete=models.CASCADE)
And serializers:
class MoocherResourceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
URL_FIELD_NAME = 'url'
model = MoocherResource
fields = ('url', )
class MoocherPageSerializer(serializers.ModelSerializer):
resources = MoocherResourceSerializer(many=True, read_only=True)
class Meta:
model = MoocherPage
fields = ('name', 'bio', 'resources')
depth = 1
I expected
{
"name": "KissofLove",
"bio": "I'm the kiss of love and I collect money for all lovers of the world.",
"resources": ["https://stackoverflow.com/users/KissofLove"]
}
But the resources was not included.
When I change read_only=True to False in the nested serializer an error appears.
AttributeError: Original exception text was: 'MoocherPage' object has no attribute 'resources'.
Your models
class MoocherPage(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=False)
name = models.CharField(max_length=48, unique=True, blank=False, null=False)
bio = models.TextField(blank=False, null=False)
class MoocherResource(models.Model):
url = models.URLField(blank=False, null=False)
moocher = models.ForeignKey(MoocherPage, on_delete=models.CASCADE)
and your serializers,
from rest_framework.serializers import *
class ResourceListingField(RelatedField):
def to_representation(self, value):
return value.url
class MoocherPageSerializer(ModelSerializer):
resources = ResourceListingField(many=True, source='moocherresource_set', read_only=True)
class Meta:
model = MoocherPage
fields = ['name', 'bio', 'resources']
This returns the desired
{
"name": "KissOfLove",
"bio": "I'm the kiss of love and I collect money for all lovers of the world.",
"resources": ["https://stackoverflow.com/users/KissofLove"]
}
response.
Check out Custom relational fields

tabular inline admin where table are referenced in multiple tables

I was looking on customizing admin where i found tabularinline model which allow us to edit on the same page. But i got confused as my tables are reference in multiple places. I could not understand which model should i make tabular inline.
for example here is my model
class ProductType(ModelWithMetadata):
name = models.CharField(max_length=150, help_text="type of product. ex - accessories, apparel")
slug = models.SlugField(max_length=150, unique=True)
is_shipping_required = models.BooleanField(default=True)
class Meta:
ordering = ("slug",)
app_label="products"
class Category(MPTTModel, ModelWithMetadata):
name = models.CharField(max_length=128)
slug = models.SlugField(max_length=128)
description = models.TextField(blank=True)
parent = models.ForeignKey(
"self", null=True, blank=True, related_name="children", on_delete=models.CASCADE
)
objects = models.Manager()
tree = TreeManager()
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Product(ModelWithMetadata, PublishableModel):
product_type = models.ForeignKey(
ProductType, related_name="products", on_delete=models.CASCADE
)
name = models.CharField(max_length=128)
slug = models.SlugField()
category = models.ForeignKey(
Category,
related_name="products",
on_delete=models.SET_NULL,
null=True,
blank=True,
)
isAvailable = models.BooleanField(default=True)
isDiscount = models.BooleanField(default=False)
charge_taxes = models.BooleanField(default=False)
updated_at = models.DateTimeField(auto_now=True, null=True)
class Meta:
verbose_name = "product"
verbose_name_plural = "products"
ordering = ("name",)
constraints = [
models.UniqueConstraint(
fields=["article_number", "slug"], name="unique article number and slug")
]
class ProductVariant(ModelWithMetadata):
sku = models.CharField(max_length=255, unique=True)
name = models.CharField(max_length=255, blank=True)
currency = models.CharField(
max_length=settings.DEFAULT_CURRENCY_CODE_LENGTH,
default=settings.DEFAULT_CURRENCY,
blank=True,
null=True,
)
price_override_amount = models.DecimalField(
max_digits=settings.DEFAULT_MAX_DIGITS,
decimal_places=settings.DEFAULT_DECIMAL_PLACES,
blank=True,
null=True,
)
product = models.ForeignKey(
Product, related_name="variants", on_delete=models.CASCADE
)
images = models.ManyToManyField("ProductImage", through="VariantImage")
track_inventory = models.BooleanField(default=True)
class BaseAssignedAttribute(models.Model):
assignment = None
values = models.ManyToManyField("AttributeValue")
class AttributeValue(models.Model):
name = models.CharField(max_length=250)
value = models.CharField(max_length=100, blank=True, default="")
slug = models.SlugField(max_length=255)
attribute = models.ForeignKey(
"Attribute", related_name="values", on_delete=models.CASCADE
)
class AssignedProductAttribute(BaseAssignedAttribute):
"""Associate a product type attribute and selected values to a given product."""
product = models.ForeignKey(
Product, related_name="attributes", on_delete=models.CASCADE
)
assignment = models.ForeignKey(
"ProductAttribute", on_delete=models.CASCADE, related_name="productassignments"
)
class Meta:
unique_together = (("product", "assignment"),)
class AssignedVariantAttribute(BaseAssignedAttribute):
"""Associate a product type attribute and selected values to a given variant."""
variant = models.ForeignKey(
ProductVariant, related_name="attributes", on_delete=models.CASCADE
)
assignment = models.ForeignKey(
"AttributeVariant", on_delete=models.CASCADE, related_name="variantassignments"
)
class Meta:
unique_together = (("variant", "assignment"),)
class AttributeVariant(models.Model):
attribute = models.ForeignKey(
"Attribute", related_name="attributevariant", on_delete=models.CASCADE
)
product_type = models.ForeignKey(
ProductType, related_name="attributevariant", on_delete=models.CASCADE
)
assigned_variants = models.ManyToManyField(
ProductVariant,
blank=True,
through=AssignedVariantAttribute,
through_fields=("assignment", "variant"),
related_name="attributesrelated",
)
class Attribute(models.Model):
name = models.CharField(max_length=30, unique=True)
slug = models.SlugField(max_length=250, unique=True)
product_types = models.ManyToManyField(
ProductType,
blank=True,
related_name="product_attributes",
through="ProductAttribute",
through_fields=("attribute", "product_type"),
)
product_variant_types = models.ManyToManyField(
ProductType,
blank=True,
related_name="variant_attributes",
through=AttributeVariant,
through_fields=("attribute", "product_type"),
)
class ProductAttribute(models.Model):
attribute = models.ForeignKey(
"Attribute", related_name="attributeproduct", on_delete=models.CASCADE
)
product_type = models.ForeignKey(
ProductType, related_name="attributeproduct", on_delete=models.CASCADE
)
assigned_products = models.ManyToManyField(
Product,
blank=True,
through=AssignedProductAttribute,
through_fields=("assignment", "product"),
related_name="attributesrelated",
)
I am confused on the tables AttributeValue, AssignedProductAttribute, AssignedVariantAttribute, AttributeVariant, Attribute and ProductAttribute. Attribute is related to ProductAttribute and is also related to AttributeVariant and AttributeValue. Similarly, in the case of variant.
I could not figure out which table should be inline and where should i reference that inlined table. Because of various relationship, i am not sure of those things.

How to update a list of instance by counting the related item upon list?

I want to update a list of instances with the total number of books links to the category.
The response is as mentioned below. I have some books link to 'History' for example. How do I update the total number on the list whenever I call it?
[
{
"id": 1,
"category_name": "History",
"category_code": "his",
"is_tab": true,
"add_time": "2020-03-02T15:56:58.469917Z",
"total_number": 0
},
{
"id": 2,
"category_name": "Romance",
"category_code": "ROM",
"is_tab": true,
"add_time": "2020-05-22T17:02:47.919479Z",
"total_number": 0
},
{
"id": 3,
"category_name": "Sci-fic",
"category_code": "S-F",
"is_tab": true,
"add_time": "2020-05-22T17:04:57.896846Z",
"total_number": 0
}
]
serializer.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = BookCategory
fields = ('id', 'category_name', 'category_code', "is_tab", 'add_time', 'total_number')
view.py
class BookCategoryDetailView(ListCreateAPIView, RetrieveModelMixin):
queryset = BookCategory.objects.all()
serializer_class = CategorySerializer
model.py
class BookCategory(models.Model):
category_name = models.CharField(default="", max_length=30, verbose_name='Category name')
category_code = models.CharField(default="", max_length=30, verbose_name='Category code')
is_tab = models.BooleanField(default=False, verbose_name='is Navigate')
add_time = models.DateTimeField(auto_now_add=True, verbose_name='Added time')
total_number = models.BigIntegerField(default=0, verbose_name='Total Number', editable=False)
class Meta:
verbose_name = 'Type Category'
verbose_name_plural = verbose_name
db_table = 'Book Genre'
def __str__(self):
return self.category_name
book model
class Book(models.Model):
BOOK_STATUS = (
('Ongoing', u'Ongoing'),
('Completed', u'Completed')
)
book_name = models.CharField(default="", max_length=30, verbose_name='Book name', unique=True)
book_image = models.ImageField(default="", max_length=30, verbose_name='Book image')
book_status = models.CharField(choices=BOOK_STATUS, default='Ongoing', verbose_name='Book Status', max_length=150,
null=True)
book_author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
verbose_name='author',
related_name='author',
null=True)
book_type = models.ForeignKey(BookCategory,
on_delete=models.CASCADE,
verbose_name='book type',
related_name='book_type',
null=True)
book_short_description = models.TextField(verbose_name='Short description', default='')
book_description = models.TextField(verbose_name='Book Description', default='')
# non-editable values
total_words = models.IntegerField(verbose_name='Total_words', default=0, editable=False)
chapter_count = models.IntegerField(verbose_name='Chapter Count', default=0, editable=False)
total_vote = models.IntegerField(verbose_name='Total vote', default=0, editable=False)
weekly_vote = models.IntegerField(verbose_name='Weekly vote', default=0, editable=False)
total_click = models.IntegerField(verbose_name='Total Click', default=0, editable=False)
fav_num = models.IntegerField(verbose_name='Total favorite number', default=0, editable=False)
added_time = models.DateTimeField(verbose_name='Added time', auto_now_add=True, editable=False)
last_update = models.DateTimeField(verbose_name='last update', auto_now=True, editable=False)
def get_chapter_number(self):
chapter_count = Chapter.objects.filter(self.id).count()
return chapter_count
def get_book_name(self):
return self.book_name
class Meta:
db_table = 'Books'
verbose_name = 'Novel'
verbose_name_plural = verbose_name
Any help would be much appreciated!
A common misconception is to give th related_name=… parameter [Django-doc] the same name as the relation itself. The related_name is the name of the relation in reverse, so the relation Django creates to obtain the Books from a given BookCategory for example. Therefore you can rename these to:
class Book(models.Model):
# …
book_author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
verbose_name='author',
related_name='authored_books',
null=True
)
book_type = models.ForeignKey(
BookCategory,
on_delete=models.CASCADE,
verbose_name='book type',
related_name='books',
null=True
)
# …
It is also better not to store duplicated (aggregate) data in the database, so storing the number of books is usually not a good idea. It turns out that keeping data in sync, even on the same database, is harder than one would expect. It means that if you create a book, delete a book, or change the category of a book, you need to update the count. Furthermore some removals will be triggered by other removals (cascading deletes), or by another tool than through the Django ORM, so that will only make the problem more complicated.
Therefore it is better to remove that field:
class BookCategory(models.Model):
category_name = models.CharField(default="", max_length=30, verbose_name='Category name')
category_code = models.CharField(default="", max_length=30, verbose_name='Category code')
is_tab = models.BooleanField(default=False, verbose_name='is Navigate')
add_time = models.DateTimeField(auto_now_add=True, verbose_name='Added time')
# no total_number
class Meta:
verbose_name = 'Type Category'
verbose_name_plural = 'Type categories'
db_table = 'Book Genre'
def __str__(self):
return self.category_name
You can obtain the number of books per BookCategory by annotating:
from django.db.models import Count
BookCategory.objects.annotate(
total_number=Count('books')
)
This means that the BookCategorys that arise from this queryset will have an extra attribute .total_number that contains the number of related Book objects.
Next we can update the serialzier to work with this total_number:
class CategorySerializer(serializers.ModelSerializer):
total_number = serializers.IntegerField(read_only=True)
class Meta:
model = BookCategory
fields = ('id', 'category_name', 'category_code', "is_tab", 'add_time', 'total_number')
and finally in the BookCategoryDetailView we pass the annotated queryset:
from django.db.models import Count
class BookCategoryDetailView(RetrieveModelMixin, ListCreateAPIView):
queryset = BookCategory.objects.annotate(total_number=Count('books'))
serializer_class = CategorySerializer

Categories