One to Many field in Model of Django - python

I am creating a model in Django, but quite confuse about the type of field should i take:
Problem: I have to access multiple domains in one web classification,
advice me please how do i make these field relationship so that if I
try to get one web classification details then it will also contain list
of related domain in that as well.
Model.py:
class WebClassification(models.Model):
vendor_id = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None, null=True)
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
domain_name = models.[what type i take here]
def __str__(self):
return self.name
Domain.py
class Domain(models.Model):
id = models.IntegerField()
domain_name = models.CharField(max_length=200)
def __str__(self):
return self.domain_name

If each web classification should include a list of related domain then you should include a ForeignKey field in the Domain model to WebClassification:
# models.py
class Domain(models.Model):
id = models.IntegerField()
web_classification = models.ForeignKey(WebClassification, on_delete=models.CASCADE)
domain_name = models.CharField(max_length=200)
def __str__(self):
return self.domain_name
And you'd create a serializer for WebClassification model:
# serializers.py
from rest_framework import serializers
from .models import Domain, WebClassification
class WebClassificationSerializer(serializers.ModelSerializer):
domains = serializers.SerializerMethodField('get_domains')
def get_domains(self, id):
return Domain.objects.filter(web_classification=id).values()
class Meta:
model = WebClassification
fields = "__all__"
And use WebClassificationSerializer in a view:
# views.py
from rest_framework import generics
from .serializers import WebClassificationSerializer
from .models import WebClassification
class WebClassificationListAPIView(generics.ListAPIView):
serializer_class = WebClassificationSerializer
queryset = WebClassification.objects.all()

You can do it in two ways. It depends if one Domain may have multiple WebClassification or just one:
# one Domain may have multiple WebClassifications
class WebClassification(models.Model):
...
domains = models.ManyToManyField("Domain")
# or
# one Domain may have one WebClassification
class Domain(models.Model):
...
web_classification = ForeignKey("WebClassification", on_delete=models.CASCADE, default=None, null=True, related_name="domains")
with both methods you can access all domains related for one WebClassification with:
web_classification = WebClassification.objects.create(...)
web_classification.domains.all()
and in template
{{ web_classification.domains.all }}

Related

How the updating of m2m fields are working? Django Rest Framework

I have code where Product model has M2M field Comment model. I made methods in views.py for getting Product by id and all objects of Product, but there's problem with updating it. I can't fully understand how to update Product comments field. I already tried a lot of different answers and ideas, but nothing helps me. I'm using PUT method for update.
My main questions:
Where and how target Product id in order to update comments field in it?
After I find Product by it's id how I can update M2M field comments in it?
I tried:
Completely Lost: Many To Many with Serializers and Update in Django Rest Framework
Doesn't work at all, screams for 'collections.OrderedDict' object has no attribute 'id'
Django Rest update many to many by id
It's making PrimaryKeyRelatedField which is not correct for me.
How to update many to many Django REST?
Not enough information in answer, got stuck on it.
I read, but found no usefull information on my problem
products/models.py
from django.db import models
from comments.models import Comment
# Create your models here.
class Product(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=150, blank=True)
description = models.TextField(default="No description")
category = models.CharField(max_length=255, blank=True)
price = models.DecimalField(default=100, max_digits=15, decimal_places=2, blank=True)
photo_one = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True, null=True)
photo_two = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True, null=True)
photo_three = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True, null=True)
photo_four = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True, null=True)
is_popular = models.BooleanField(default=False)
is_hit = models.BooleanField(default=False)
has_sale = models.IntegerField(default=0)
comments = models.ManyToManyField(Comment, blank=True)
def __str__(self):
return self.name
comments/models.py
import uuid
from django.db import models
# Create your models here.
class Comment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
username = models.CharField(max_length=255, default="default")
text = models.TextField()
rating = models.IntegerField()
def __string__(self):
return self.username
products/serializers.py
class ProductSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True)
class Meta:
model = Product
fields = '__all__'
def update(self, instance, validated_data):
submitted_comments = validated_data.get('comments')
if submitted_comments:
for comment in submitted_comments:
comment_instance = Comment.objects.get(id=comment.id)
instance.children.add(comment_instance)
instance.save()
return instance
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('id', 'username', 'text', 'rating')
products/views.py
import django_filters
from rest_framework import status
from rest_framework.generics import ListAPIView, RetrieveUpdateAPIView
from rest_framework.response import Response
from .models import Product
from .pagination import CustomPagination
from .serializers import ProductSerializer
class GetProductById(RetrieveUpdateAPIView):
serializer_class = ProductSerializer
filter_class = ProductFilter
queryset = Product.objects.all()
def get_product(self, pk):
try:
product = Product.objects.get(pk=pk)
except Product.DoesNotExist:
content = {
'status': 'Not Found'
}
return Response(content, status=status.HTTP_404_NOT_FOUND)
return product
def get(self, request, pk):
product = self.get_product(pk)
serializer = ProductSerializer(product)
return Response(serializer.data, status=status.HTTP_200_OK)
class ProductFilter(django_filters.FilterSet):
product_id = django_filters.UUIDFilter(name='id')
class Meta:
model = Product
fields = ['id']
class GetProducts(ListAPIView):
serializer_class = ProductSerializer
pagination_class = CustomPagination
def get_queryset(self):
products = Product.objects.all()
return products
def get(self, request):
products = self.get_queryset()
paginate_queryset = self.paginate_queryset(products)
serializer = self.serializer_class(paginate_queryset, many=True)
return self.get_paginated_response(serializer.data)
*products/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('api/products/', views.GetProducts.as_view(), name='get_products'),
path('api/product/<pk>/', views.GetProductById.as_view(), name='get_put_product')
]
I expect successful target by Product id and finding product.comments field
I expect successful adding new Comment to Product in it's comments field

Django Rest Framework - Create foreign key object on POST

I have a simple DRF REST API that I want to use to create blog articles. I want to be able to add tags to those blog articles so users can search tags and see related articles. However, the tags may not exist yet. I have created an Article Model with a ForeignKey field to a Tag Model like this:
class Tag(models.Model):
name = models.CharField(max_length=32)
def _str__(self):
return self.name
class Meta:
ordering = ('name',)
class Article(models.Model):
title = models.CharField(max_length=256)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
date = models.DateTimeField(auto_now_add=True)
tags = models.ForeignKey(Tag, on_delete=models.CASCADE, blank=True, default=None)
def __str__(self):
return self.title
class Meta:
ordering = ('date', 'id')
Ideally what I want is to be able to POST a new Article with a set of tags, and if any of the tags don't exist, create them in the DB. However, as it is currently, the tags need to already exist to be added to the Article. Visually, DRF shows this as a dropdown that is populated with pre-existing tags:
How can I add or create multiple Tags from my Article API endpoint?
EDIT: As requested, I've added my views.py
views.py:
from api.blog.serializers import ArticleSerializer, TagSerializer
from rest_framework import viewsets
# /api/blog/articles
class ArticleView(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
# /api/blog/tags
class TagView(viewsets.ModelViewSet):
queryset = Tag.objects.all()
serializer_class = TagSerializer
For completeness, here are my serializers from my REST API's serializers.py.
serializers.py:
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
urls.py:
from rest_framework import routers
router = routers.DefaultRouter()
router.register('articles', views.ArticleView)
router.register('tags', views.TagView)
urlpatterns = [
path('', include(router.urls)),
]
Overriding the create() method of the serializer as
class ArticleSerializer(serializers.ModelSerializer):
tags = serializers.CharField()
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
tag = validated_data.pop('tags')
tag_instance, created = Tag.objects.get_or_create(name=tag)
article_instance = Article.objects.create(**validated_data, tags=tag_instance)
return article_instance
Okay, thanks to #JPG for their help. This is what I've ended up with. It allows users to add space delimited tags into a CharField on the /api/blog/article endpoint. When a POST request is performed, the tags are split on spaces, get_or_create()d (for this to work I needed to make Tag.name the primary key), and then added to the Article with article.tags.set(tag_list). As #JPG and #Martins suggested, a ManyToManyField() was the best way to do this.
Here is my full code:
serializers.py:
class ArticleSerializer(serializers.ModelSerializer):
class TagsField(serializers.CharField):
def to_representation(self, tags):
tags = tags.all()
return "".join([(tag.name + " ") for tag in tags]).rstrip(' ')
tags = TagsField()
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
tags = validated_data.pop('tags') # Removes the 'tags' entry
tag_list = []
for tag in tags.split(' '):
tag_instance, created = Tag.objects.get_or_create(name=tag)
tag_list += [tag_instance]
article = Article.objects.create(**validated_data)
print(tag_list)
article.tags.set(tag_list)
article.save()
return article
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
Note that I had to create a custom TagField() and override to_representation(). This is because if I used a regular serializer.CharField() tags were displayed as: "Blog.tag.None" instead of the tag values, like this:
models.py:
class Tag(models.Model):
name = models.CharField(max_length=32, primary_key=True)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class Article(models.Model):
title = models.CharField(max_length=256)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
date = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.title
class Meta:
ordering = ('date', 'id')

How to add string to a ModelSerializer in Django REST

In my database, I store the file name of a particular image for an item. Let's say this is the model in models.py
from django.db import models
class Product(models.Model):
sku = models.CharField(validators=[isalphanumeric], max_length=20, null=False, blank=False)
image = models.CharField(max_length=20, blank=False, null=False)
and then I have a serializer defined like so in serializers.py
from rest_framework import serializers
from app.models import Product
class ProductSerializer(serializer.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
what I want is to be able to add a string to the image of a Product that makes it into a string representing the relative link, something like:
storage_location = '/assets/img'
img_url = f'{storage_location}/{image}'
The reason why I want to do this is because I want to be flexible with the urls rather than having the file name be a "file location" and then having to update the database each time I change how I arrange my images (I'm still not sure how to store them).
How can I do that?
First of all you can use model's ImageField for this:
class Product(models.Model):
sku = models.CharField(validators=[isalphanumeric], max_length=20, null=False, blank=False)
image = models.ImageField(max_length=20, blank=False)
This will automatically add MEDIA_URL setting to the value when you fetch value.
In case you want to use CharField you can do what you need on serializer level using SerializerMethodField:
class ProductSerializer(serializer.ModelSerializer):
image = serializers.SerializerMethodField()
def get_image(self, obj):
storage_location = '/assets/img'
img_url = f'{storage_location}/{obj.image}'
return img_url
class Meta:
model = Product
fields = '__all__'
Try following in your serialiser.py
class ProductSerializer(serializer.ModelSerializer):
img_url = serializers.SerializerMethodField()
class Meta:
model = Product
fields = '__all__'
#fields = ('sku', 'img_url') # This is what i will prefer
def get_img_url(self, obj):
storage_location = '/assets/img'
img_url = f'{storage_location}/{obj.image}'
return img_url
Good Luck!

Which is the easier and convenient way to make post methods for multiple model instances in django

I have an Employee class that has a couple of instances that are ForeignKeys. I am using django rest frame work and I have created also a serializer and the next step is to create a POST method to enable creating of an Employee. Which method can I use conveniently to achieve this?
class Employee(models.Model):
"""
Model, which holds general information of an employee.
"""
user = models.OneToOneField(User,related_name='users',
on_delete=models.CASCADE)
photo_logo = models.FileField()
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=True) # validators should be a list
company = models.ForeignKey(
'hr.Company',
verbose_name='Company',
related_name='companies',
null=True, blank=True,
)
marital_status = models.ForeignKey(MaritalStatus)
identification_type = models.ForeignKey(IdentificationType)
Serializers.py
class EmployeeSerializer(serializers.ModelSerializer):
designation = GroupListSerializer()
# department = GroupListSerializer()
# user = UserSerializer()
# address = AddressSerializer()
# em_contact = EmergencyContactSerializer()
# address = EmergencyContactSerializer()
class Meta:
model = Employee
fields = ['id','user','photo_logo','phone_number','dob', 'gender', 'hr_number', 'company', 'marital_status', 'identification_type',
'tax_id_number','joining_date', 'designation', 'department']
I recommend you using CreateAPIView from Generic views: http://www.django-rest-framework.org/api-guide/generic-views/#createapiview
So, you'll have to add class to your views.py. It may look like this:
from rest_framework import generics
from .serializers import EmployeeSerializer
class CreateEmployeeView(generics.CreateAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
Also, don't forget to register your view in urls.py

Django limit next inlinefield to first inlinefield

I have this model in django
from django.db import models
class ProductType(models.Model):
name = models.CharField(max_length=250)
slug = models.SlugField(unique=True)
def __unicode__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=250)
slug = models.SlugField(unique=True)
type = models.ForeignKey(ProductType, related_name='product_type')
related_products = models.ManyToManyField('self',blank=True,null=True)
description = models.TextField()
def __unicode__(self):
return self.name
What I would like to do is in the admin have the Products as tabular inline to a quote page that have multiple products. This thing is that the first selected model can be a parent to the others and therefore I would like the choices of the next elements to be sorted by the first.
this is the quote model
from django.db import models
from django.utils.translation import ugettext as _
import datetime
from products.models import Product
from customers.models import Customer
class Quote(models.Model):
quoteid = models.IntegerField(_('Quote ID'), max_length=8, unique=True, default=number)
slug = models.SlugField(unique=True,default=number)
add_date = models.DateTimeField(auto_now_add=True)
customer = models.ForeignKey(Customer, related_name='quote_customer',blank = True, null = True)
product = models.ManyToManyField(Product, related_name='quote_product')
def __unicode__(self):
return str(self.quoteid)
and the admin part
from django.contrib import admin
from quotes.models import Quote
class ProductInline(admin.TabularInline):
model = Quote.product.through
extra = 3
class QuoteAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('quoteid',)}
fieldsets = (
(('Quote'), {'fields': ('slug','quoteid','customer',)}),
)
list_display = ('quoteid','customer','add_date',)
inlines = [ ProductInline]
admin.site.register(Quote,QuoteAdmin)
I know this is quite tricky and I have tried many ways but I have not found a solution that works. I have tried with formfield_for_manytomany but I can't fully grasp how to return the first tabularinline object as the input for the queryset.
If someone have a link that explains a method to do this I would be grateful.

Categories

Resources