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
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.
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)
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
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
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)