How to add check constraint validator in serializer in django rest framework? - django-models

I have a model defined as follows :
class Order(models.Model):
qty = models.FloatField()
pending_qty = models.FloatField(default=0)
class Meta:
constraints = [
models.CheckConstraint(check=models.Q(
pending_qty__gte=0), name='pending_qty__gte_0')
]
The serializer for this models is as follows:
class NewOrderSz(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
I want to add a validator in the serializer to validate for the 'pending_qty__gte_0' CheckConstraint in the model. How can I do that ?
View for creating an entity is :
class NewOrder(generics.CreateAPIView):
queryset = models.Order.objects.none()
serializer_class = serializers.NewOrderSz

Just define your pending_qty with FloatField with a min_value constraint
and you should be fine
class NewOrderSz(serializers.ModelSerializer):
pending_qty = serializers.FloatField(min_value=0)
class Meta:
model = Order
fields = '__all__'

class NewOrderSz(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
def validate_pending_qty(self, value):
if value < 0:
raise serializers.ValidationError("Pending Qty Less Than Zero!")
return value
https://www.django-rest-framework.org/api-guide/serializers/#field-level-validation

Related

How to make required filling in a form field with a many-to-many relationship in django admin

I have a Recipes model with an ingredients field with a many-to-many relationship. I have an intermediate Ingredient amount model associated with the ingredients field via through. I want to prohibit the creation of a recipe in django admin without ingredients. I'm trying to solve this problem by creating my own class inherited from BaseInlineFormSet and redefining the clean() method. It doesn't work out yet, I need help, what am I doing wrong?
admin.py
class IngredientsAmountInlineFormset(BaseInlineFormSet):
def clean_ingredients(self):
if len(self.cleaned_data['ingredients']) < 1:
return 'Укажите хотя бы один ингредиент в рецепте'
return self.cleaned_data['ingredients']
class IngredientsAmountInline(admin.TabularInline):
model = IngredientsAmount
formset = IngredientsAmountInlineFormset
extra = 0
#admin.register(Recipes)
class RecipesAdmin(admin.ModelAdmin):
list_display = ('name', 'author', 'favorites_count',)
list_filter = ('name', 'author', 'tags',)
search_fields = ('name', 'author', 'tags',)
empty_value_display = '-пусто-'
inlines = (IngredientsAmountInline,)
models.py
class Ingredients(models.Model):
name = models.CharField(max_length=200, unique=True)
measurement_unit = models.CharField(max_length=200)
...
class Recipes(models.Model):
...
ingredients = models.ManyToManyField(Ingredients,
through='IngredientsAmount',
related_name='recipes',
verbose_name='Ингредиенты',
)
...
class IngredientsAmount(models.Model):
ingredients = models.ForeignKey(Ingredients,
on_delete=models.CASCADE,
related_name='amount',
verbose_name='Ингредиенты',
)
recipes = models.ForeignKey(Recipes,
on_delete=models.CASCADE,
related_name='amount',
verbose_name='Рецепты', )
amount = models.IntegerField()
obiviously clean should raise ValidationException:
class IngredientsAmountInlineFormset(BaseInlineFormSet):
def clean_ingredients(self):
value = self.cleaned_data['ingredients']
if value:
return value
raise ValidationError(_('Укажите хотя бы один ингредиент в рецепте'))
by the way:
Try don't use the direct text message:
'Укажите хотя бы один ингредиент в рецепте'
Wrap your text in gettext/gettext_lazy, like this:
from django.utils.translation import gettext_lazy as _
_('Укажите хотя бы один ингредиент в рецепте')
It help you in future collect those messages together, and translate it if you need.

How to perform query on one to many relations tables using ORM and define serializers for it?

data in request
city_name = 'Nashik'
tags = ['tag 1','tag 2', 'tag 3']
I try this
ws = WorkSamplesModel.objects.filter(business_account__serviceareasmodel__city_name=city_name,
business_account__professiontagsmodel__tag_name__in=tags_list,
is_business_card_image=True).distinct()
Now I want business_title ,business_description,status,note from BusinessAccountModel and
work_sample_image, work_sample_description, is_business_card_image from WorkSampleModel and
user_name,profile_pic from UserModel
how to create serializer for it?
models
I want to find a specific BusinessAccountModel using city_name and tags
and then one WorkSamplesModel for each BusinessAccountModel.
class BusinessAccountModel(models.Model):
business_title = models.CharField(max_length=70)
business_description = models.CharField(max_length=500)
status = models.CharField(max_length=100)
note = models.CharField(max_length=200)
user = models.OneToOneField(UserModel,on_delete=models.CASCADE)
class Meta:
db_table = 'wp_business_acc'
class ProfessionTagsModel(models.Model):
tag_name = models.CharField(max_length=20)
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE)
class Meta:
db_table = 'profession_tags'
class ServiceAreasModel(models.Model):
city_name = models.CharField(max_length=20)
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE)
class Meta:
db_table = 'service_areas'
class WorkSamplesModel(models.Model):
work_sample_image = models.ImageField(blank=True,null=True,upload_to="work_samples")
work_sample_description = models.CharField(max_length=1000)
is_business_card_image = models.BooleanField(default=False)
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE)
class Meta:
db_table = 'work_samples'
BusinessAccountModel - ProfessionTagsModel : 1:M
BusinessAccountModel - ServiceAreasModel : 1:M
BusinessAccountModel - WorkSamplesModel : 1:M
ws = WorkSamplesModel.objects.filter(business_account__serviceareasmodel__city_name=city_name,
business_account__professiontagsmodel__tag_name__in=tags_list,
is_business_card_image=True).distinct()
Serializer for it
class SearchWorkingProgessionalsSerializers(serializers.Serializer):
business_title = serializers.CharField(source='business_account.business_title')
business_description = serializers.CharField(source='business_account.business_description')
status = serializers.CharField(source='business_account.status')
note = serializers.CharField(source='business_account.note')
work_sample_image = serializers.ImageField()
work_sample_description = serializers.CharField(max_length=1000)

How check UniqueConstraint in Django with CreateView and custom forms if field set in view?

I do:
I define UniqueConstraint (also try with 'unique_together') in model:
class Project(models.Model):
class Meta:
constraints = [
models.UniqueConstraint(
fields=['company', 'name'], name="unique_project_name_in_company"
)
]
name = models.CharField(blank=False, max_length=256)
company = models.ForeignKey(
Company,
on_delete=models.CASCADE
)
I set company in form_valid in view (I think it's reason of my problem):
class ProjectCreateView(LoginRequiredMixin, generic.CreateView):
model = Project
form_class = ProjectForm
def form_valid(self, form):
form.instance.company = self.request.user.company
return super().form_valid(form)
I try define message for 'unique_project_name_in_company' in form:
class ProjectForm(forms.ModelForm):
model = Project
class Meta:
model = Project
fields = ['name']
error_messages = {
NON_FIELD_ERRORS: {
'unique_project_name_in_company': "Name isn't unique!",
}
}
Unexpected behavior
If I submit form with non-unique pair (inputed non-unique name) I want get my custom error_message but I get:
500 IntegrityError UNIQUE constraint failed: company_id, name

How do i access another column from related table other than the foreign key, when creating an API view

Im using django for a web app and i am creating REST API views. Is there a way i can access two tables in one view? If not, how can can i retrieve a non-foreign key column from a related record. The below code is retrieving a vase record based on a URL parameter. I want to access the artistName which is stored in artist table (a one-to-many with Vase table), not artist_id which is stored in Vase
class FilterVases(generics.ListAPIView):
serializer_class = VaseSerializer
def get_queryset(self):
queryset = Vase.objects.all()
artist_id = self.request.query_params.get('artist_id')
if artist_id is not None:
queryset = queryset.filter(artist_id=artist_id)
vaseID = self.request.query_params.get('vaseID')
if vaseID is not None:
queryset = queryset.filter(vaseID=vaseID)
return queryset
edited to add
This is models for Artist and Vase:
class Artist(models.Model) :
artistID = models.CharField(max_length=10)
artistName = models.CharField(max_length=100)
class Vase(models.Model):
vaseID = models.CharField(max_length=10)
vaseRef = models.CharField(max_length=255,blank=True,null=True)
inscription = models.CharField(max_length=255,blank=True,null=True)
fabric = models.CharField(max_length=100, blank=True,null=True)
subject = models.CharField(max_length=255,blank=True,null=True)
technique = models.CharField(max_length=100,blank=True,null=True)
height = models.FloatField(max_length=100,blank=True,null=True)
diameter = models.FloatField(max_length=100,blank=True,null=True)
shape = models.ForeignKey(Shape, on_delete=models.CASCADE)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
provenance = models.ForeignKey(Provenance, on_delete=models.CASCADE)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
In the Vase model add this:
def artist_name(self):
return self.artist.artistName
Hence, it will look like:
class Vase(models.Model):
vaseID = models.CharField(max_length=10)
vaseRef = models.CharField(max_length=255,blank=True,null=True)
inscription = models.CharField(max_length=255,blank=True,null=True)
fabric = models.CharField(max_length=100, blank=True,null=True)
subject = models.CharField(max_length=255,blank=True,null=True)
technique = models.CharField(max_length=100,blank=True,null=True)
height = models.FloatField(max_length=100,blank=True,null=True)
diameter = models.FloatField(max_length=100,blank=True,null=True)
shape = models.ForeignKey(Shape, on_delete=models.CASCADE)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
provenance = models.ForeignKey(Provenance, on_delete=models.CASCADE)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
def artist_name(self):
return self.artist.artistName
In the VaseSerializer add the 'artist_name' to the fields Meta.
If you want to add this custom fields to all Vase Model fields, refer to this topic Django Rest framework, how to include '__all__' fields and a related field in ModelSerializer ?
class VaseSerializer(serializers.ModelSerializer):
class Meta:
model = models.Vase
fields = '__all__'
extra_fields = ['artist_name']
def get_field_names(self, declared_fields, info):
expanded_fields = super(VaseSerializer, self).get_field_names(
declared_fields, info)
if getattr(self.Meta, 'extra_fields', None):
return expanded_fields + self.Meta.extra_fields
else:
return expanded_fields
Below should your view:
class FilterVases(generics.ListAPIView):
serializer_class = VaseSerializer
def get_queryset(self):
queryset = Vase.objects.all()
query_artist = self.request.query_params.get('artist_name')
if query_artist is not None:
try:
artist = Artist.objects.get(artistName=query_artist)
queryset = queryset.filter(artist=artist)
except:
pass
vaseID = self.request.query_params.get('vaseID')
if vaseID is not None:
queryset = queryset.filter(vaseID=vaseID)
return queryset

forms.ModelChoiceField Update view with defualt value

I have Form ModelChoiceField from ForeignKey model
my Model is:
class Fielinfo(models.Model):
Perimeter = models.CharField(max_length=50,unique=True)
FidlAbr = models.CharField(max_length=15,unique=True)
and my second model :
class Wellinfo(models.Model):
WellID = models.CharField(max_length=15,unique=True)
Perimetre = models.ForeignKey(Fielinfo ,to_field='Perimeter', on_delete=models.CASCADE)
and my forms.py is:
class NewWells(forms.ModelForm):
Perimetre = forms.ModelChoiceField(queryset=Fielinfo.objects.all(),label='Perimetre', required=True)
the problem is When I need to update my view it doesn't gave me the initial value?
How do I solve this Please? (in the admin area it gave me the initial value).
my views.py is:
class PostUpadtW(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
model = Wellinfo
template_name = 'Home/WELLINFO/Upd_W.html'
form_class = NewWells
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user== post.author:
return True
else:
return False
using ForeignKey is so complicated in Django so i solved it like this;
in models.py
class Fielinfo(models.Model):
Perimeter = models.CharField(max_length=50,unique=True)
FidlAbr = models.CharField(max_length=15,unique=True)
and I changed this
class Wellinfo(models.Model):
WellID = models.CharField(max_length=15,unique=True)
Perimetre = models.CharField(max_length=50)
and in my forms.py i did this :
class NewWells(forms.ModelForm):
TFTFields =Fielinfo.objects.all()[0:]
ListField= []
for fields in TFTFields:
ListField += [fields.Perimeter]
ChoiceItems= list(zip(ListField, ListField))
Perimetre = forms.ChoiceField(label='Perimetre',choices=ChoiceItems)

Resources