Django Check for duplicates with 2 model fields - django

I have a model that looks like this:
class UssdCode(models.Model):
title = models.CharField(max_length=100)
code = models.CharField(max_length=100)
product = models.CharField(max_length=100)
How can I get the admin to alert me and reject my entry when I try to add a new object that has the same 'code' and 'product' as an object already in the database.

You make it unique together. Since django-2.2, you can use the UniqueConstraint [Django-doc] of the Django Constraint framework [Django-doc] for that:
# since Django-2.2
class UssdCode(models.Model):
title = models.CharField(max_length=100)
code = models.CharField(max_length=100)
product = models.CharField(max_length=100)
class Meta:
constraints = [
models.UniqueConstraint(fields=['code', 'product'], name='code_product')
]
Prior to django-2.2, you can use the unique_together meta option [Django-doc]:
# before Django-2.2 (still works on Django-3.0)
class UssdCode(models.Model):
title = models.CharField(max_length=100)
code = models.CharField(max_length=100)
product = models.CharField(max_length=100)
class Meta:
unique_together = [['code', 'product']]

Related

fields in class Meta got invalid

models.py
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(decimal_places=5,max_digits= 1500)
summary = models.TextField()
featured = models.BooleanField()
def __str__(self):
return self.title
# return f'product title:{self.title}-product price:{self.price}'workok
class Meta:
ordering = ('-price',)
class Opinion(models.Model):
name = models.CharField(max_length=20)
email = models.EmailField(max_length=20)
body = models.TextField()
opinion_date = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='opinion_set')
def __str__(self):
return f'({self.name}) add opinion about ({self.product})'
forms.py:
from django.forms import ModelForm
from .models import Product #space after from keyword
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
invalid in code line :
fields = ['name','email','body','product'] #---- NOT WORK !!!
, but if i change above code to :
fields = "__all__" # ----it is WORKing ok without any problem !!
question : what is the error? I am not need all the fields in the Product model (like active boolean field), I need only 'name','email','body','product' fields .
According to the error and the code you provided the main problem is that you made a mistake in chosing model in serializer:
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
Serializer name is OpinionModelForm and listed fields belong to Opinion so I guess you actually wanted to serialize Opinion and no Product as you defined at this line:
model = Product
Simply change it to:
model = Opinion

How to display many fields' values with ForeignKey relationship?

Looking for solution of this problem I encountered some similar threads, but referring to older versions of Django/DRF and thus not working in my case.
There are these two models:
class CsdModel(models.Model):
model_id = models.CharField("Item ID", max_length=8, primary_key=True)
name = models.CharField("Item Name", max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class CsdListing(models.Model):
model_id = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_id')
name = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_name')
(...)
EDIT: Serializers are defined this way:
class CsdModelSerializer(serializers.ModelSerializer):
model_id = serializers.RegexField(regex='^\w{2}\d{3}$', allow_blank=False)
name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)
class Meta:
model = CsdModel
fields = '__all__'
class CsdListingSerializer(serializers.ModelSerializer):
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
def validate_session_id(self, value):
(...)
class Meta:
model = CsdListing
fields = '__all__'
What I'd like to see, is model_id and name from CsdModel displayed inside a form created based on CsdListing model. But instead, the ID is duplicated:
How should I rebuild the model(s) to have both ID and name displayed in the form?
You should have only one foreign key. But the listing serializer should then reference the model as a nested serializer.
class CsdListing(models.Model):
model = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='listing')
class CsdListingSerializer(serializers.ModelSerializer):
model = CsdModelSerializer()
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)

Django Rest Framework - how to get only one field from related models set

I have following models:
from django.db import models
class City(models.Model):
name = models.CharField(max_length=30)
last_update = models.DateTimeField(null=True)
class BusStop(models.Model):
city = models.ForeignKey(City, on_delete=models.CASCADE)
name = models.CharField(max_length=200, blank=True, default='')
Now using Django Rest Framework, I would like to create serializer that will return City details along with the list of all BusStops in the city - but I want the list to be only strings with BusStop names, like this:
{
"id": 1
"name": "City"
"last_update": "2019-09-19T22:13:54.851363Z"
"bus_stops": [
"stop1",
"stop2",
"stop3"
]
}
What I've tried so far is following serializers:
from rest_framework import serializers
class BusStopSerializer(serializers.ModelSerializer):
class Meta:
model = BusStop
fields = ('name', )
class CityDetailsSerializer(serializers.ModelSerializer):
busstop_set = BusStopSerializer(many=True)
class Meta:
model = City
fields = ('id', 'name', 'last_update', 'busstop_set')
But this creates list of objects with 'name' in them. So, how can I create a list with only BusStop names (as strings) in it?
Instead of the extra BusStopSerializer you could use a StringRelatedField:
# models.py
class BusStop(models.Model):
city = models.ForeignKey(City, on_delete=models.CASCADE)
name = models.CharField(max_length=200, blank=True, default='')
def __str__(self):
return self.name
# serializers.py
class CityDetailsSerializer(serializers.ModelSerializer):
bus_stops = StringRelatedField(many=True)
class Meta:
model = City
fields = ('id', 'name', 'last_update', 'bus_stops')
StringRelatedField, as recommended by wfehr, will only work as long as the __str__ method of BusStop only returns the name. An alternative is to use SlugRelatedField which allows you to specify a particular field from the related model, and has no dependency on __str__.
bus_stops = SlugRelatedField(many=True, slug_field='name')

Django Rest Framework search_fields viewset from genericforeignkey field model

All models (Customer, Provider, Contact, Employee) have the same name field to search, in the main or generic model (Comments) have generic foreign key. I need search the field in the main model. It's that posible?
Models:
class Customer(TimeStampedModel):
name = models.CharField()
class Provider(TimeStampedModel):
name = models.CharField()
class Contact(TimeStampedModel):
name = models.CharField()
class Employee(TimeStampedModel):
name = models.CharField()
class Comment(TimeStampedModel):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
commentator = GenericForeignKey('content_type', 'object_id')
Viewset
class CommentsViewSet(BaseViewSet):
queryset = Comments.objects.all()
serializer_class = CommentsSerializer
search_fields = ["commentator__name",]
Message error:
django.core.exceptions.FieldError: Field 'commentator' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.
You need to add a GenericRelation field to each of your models that are able to receive comments, for instance:
class Customer(TimeStampedModel):
name = models.CharField()
comments = GenericRelation('Comment', related_query_name='customer')
class Provider(TimeStampedModel):
name = models.CharField()
comments = GenericRelation('Comment', related_query_name='provider')
class Contact(TimeStampedModel):
name = models.CharField()
comments = GenericRelation('Comment', related_query_name='contact')
class Employee(TimeStampedModel):
name = models.CharField()
comments = GenericRelation('Comment', related_query_name='employee')
Then you define your search_fields property as the list of all the related_query_names in this way:
search_fields = ["customer__name", "provider__name", "contact__name", "employee__name"]
Please refer to this part of the documentation to know more about GenericRelation
You need to add a GenericRelation to each of your models.
class Customer(TimeStampedModel):
name = models.CharField()
comment = GenericRelation(Comment, related_query_name='customer')
class Provider(TimeStampedModel):
name = models.CharField()
comment = GenericRelation(Comment, related_query_name='provider')
...
search_fields = ["customer__name", "provider__name", "contact__name", "employee_name"]

How to set SlugRelated field to a field within an object field

I have the following models:
class Category(models.Model):
name = models.CharField()
... # fields omitted
class Prediction(models.Model):
conversation = models.ForeignKey(Conversation)
category = models.ForeignKey(Category)
... # fields omitted
class Conversation(models.Model):
sid = models.CharField()
... # fields omitted
Now I'm trying to create a model serializer for Category that would return me the following serialized object:
{
"name":"blah",
"conversations":[
"2af22188c5c97256", # This is the value of the sid field
"073aef6aad0883f8",
"5d3dc73fc8cf34be",
]
}
Here is what I have in my serializer:
class CategorySerializer(serializers.ModelSerializer):
conversations = serializers.SlugRelatedField(many=True,
read_only=True,
source="prediction_set",
slug_field='conversation.sid')
class Meta:
model = models.Class
fields = ('class_name', 'conversations')
However, this doesn't work because somehow django doesn't allow me to set slug_field to be a field that's within an object field. Any suggestions on how to accomplish this?
You are modelling a Many-to-Many relationship between Categorys and Conversations with a explicit table called Prediction. The django way of doing this would be to explicitly state the Many-to-Many on either side of the relationship and specify Prediction as the "through-model":
Shamelessly stolen example from this question:
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=255, blank=True,default=None)
desc = models.TextField(blank=True, null=True )
...
class Post(models.Model):
title = models.CharField(max_length=255)
pub_date = models.DateTimeField(editable=False,blank=True)
author = models.ForeignKey(User, null=True, blank=True)
categories = models.ManyToManyField(Category, blank=True, through='CatToPost')
...
class CatToPost(models.Model):
post = models.ForeignKey(Post)
category = models.ForeignKey(Category)
...
This shows a good way to set up the relationship.
Equally shamelessly stolen from the answer by #Amir Masnouri:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name','slug')
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id','{anything you want}','categories')
depth = 2
This shows a nice way of achieving the nested serialization behavior that you would like.