Override django form's clean method to change the ModelChoiceField queryset - django-forms

I have this model:
class PetAttribute(models.Model):
species = models.ForeignKey(PetSpecies, related_name = "pets")
pet_category = models.ForeignKey(PetCategory, related_name = "pets", blank=True, null=True)
pet_type = models.ForeignKey(PetType, related_name = "pets", blank=True, null=True)
additional_fields= models.ManyToManyField( AdditionalField, null= True, blank=True )
Now i want to add an additional option in select (pet_category, pet_type), which is
("0","Other")
in queryset of these. I have tried but form give me an error
Error: Select a valid choice. That choice is not one of the available choices.
here is a one solution of it, but i want to do this by ModelChoiceField
Any suggestion?
Thanks :)

While it is possible, are you sure this is what you want to do? You are telling a field that validates choices to the model objects to accept a non-valid answer.
Creating an "other" PetType and PetCategory object or using empty_label as "other" might make more sense than forcing the ModelChoiceField to accept arbitrary values.
Then to find objects with "other" selected, query for None,
pattrs_w_other_pet_type = PetAttribute.object.filter(pet_type=None)

Related

Django Rest Framework - Updating a ForeignKey Field entry in the view

In my Django Rest Framework project, I have a ForeignKey relationship between two models:
class Book(models.Model):
...
category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True)
...
class Category(models.Model):
name = models.CharField(max_length=100, blank=False)
As you can see, a Book can belong to a Category but it does not have to. That means the 'category' field could be null.
So, in my views.py, any Book instance can be updated/patched if the user wants to assign a certain Book to a particular Category. That views.py update method looks like this:
class UpdateBooksCategory(generics.GenericAPIView):
'''
Class-based view to update the 'category' field of a Book instance.
'''
serializer_class = BookSerializer
permission_classes = [IsAuthenticated]
def patch(self, request,*args, **kwargs):
# get the Book instance first
book = Book.objects.get(pk=request.data.get('bookId'))
# if it is not assigned to a Category, then assign it
if book and not book.category:
book.category = Category.objects.get(name=request.data.get('categoryName'))
book.save()
serializer = self.get_serializer(book, context={"request": request})
return Response(serializer.data)
# otherwise, return a generic response
return Response({'response': "You have already put the selected Book in a Category."})
If you can see, first I get the Book instance that the user wants to update by using the Book's ID. If its Category field is not already filled, I get a Category instance using the given category name and assign it.
For the sake of completeness, here are my serializer classes:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', /*some other fields*/,..., 'category']
So, finally my question: I wanted to know if this is the preferred way of updating a ForeingKey field like this? I mean looking at the UpdateBooksCategory class-based view, is this the right way of doing it? The code works ( I tested it with PostMan) but since I am new to DRF I wanted to know if such an updating process is correct.
You can change your BookSerializer:
class BookSerializer(serializers.ModelSerializer):
category_id = serializers.IntegerField(write_only=True)
category = CategorySerializer(read_only=True)
class Meta:
model = Book
fields = [
'id',
# some other fields,
'category',
'category_id',
]
category will be a nested data that is read only, then setting the category will be by including the category_id in your requests.

django rest framework ordering non model field(serializer field)

i want to ordering on my fields like this:
class DealerBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
filter_backends = (filters.OrderingFilter,
)
ordering_fields = ('online',...)
this way of ordering work only on model's fields but online field defined in my serializer and while test in postman not work.
i want to done it like this :
class CustomOrdering(filters.OrderingFilter):
def filter_queryset(self, request, queryset, view):
params = request.query_params.get(self.ordering_param)
if params == 'online':
... my serializer codes
return super(CustomOrdering, self).filter_queryset(request, queryset, view)
this problem is other fields ordering not work!! is there a way to solve it any way?
if related docs help me please give me the link .
thanks for your site
after struggle in this challenge i undrestand that exist a way to some how indicate this fields as model field and not need to CustomOrdering and any extra codes!
in my get_queryset function i change the code:
queryset = Dealer.objects.all()
to:
queryset = Dealer.objects.all().annotate(bids_count=Count('bid'), device_count=Count('device'))
note that this two fields in my serializer not in my model.
in my serilizer change this two field from SerializerMethodField to IntegerField and clean the defs.
then in my api file add this:
filter_backends = (filters.OrderingFilter,)
ordering_fields = ('bids_count', 'device_count')
this my last serializer:
class DealerListSerializer(serializers.ModelSerializer):
device_count = serializers.IntegerField()
bids_count = serializers.IntegerField()
class Meta:
model = Dealer
fields = ('id', 'last_name', 'first_name', 'username', 'person_trust', 'is_active',
'work_type', 'address', 'mobile', 'device_count', 'online', 'bids_count')
by this way my code is very clear and my CustomOrdering and all elif statements also clean!
It doesn't work because the fields defined in your serializer aren't part of the model. The ordering attribute only works for model fields. You'd probably have to introduce a work around like creating a dynamic field using annotations and then order using that field but this depends on whether or not your online field logic can be annotated.

Manipulate data on serializer before creating model instance in DangoRestFramework

I have three related models as such
Order model
class Order(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
Name = models.CharField(max_length=250)
orderType = models.ForeignKey(OrderType, on_delete=models.CASCADE, null=True)
class Meta:
ordering = ['id']
def __str__(self):
return '{}'.format(self.Name)enter code here
OrderPricing Model
class OrderPricing(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
TotalPrice = models.DecimalField(decimal_places=2, max_digits=10)
#related field
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=True)
class Meta:
ordering = ['order']
def __str__(self):
return self.TotalPrice
OrderType Model
class OrderType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
Name = models.CharField(max_length = 100)
Premium = models.BooleanField()
class Meta:
ordering = ['id']
Let's ignore the order in which the models appear above.
I have three SerializerModels for each model.
I can crud each model on the BrowsableAPI
Q1:
From the browsableAPI I can create an Order.
I haven't gotten to the 'Writable Nested Serializer' yet and I believe Django has that figured out in their docs through the drf-writable-nested class.
I have two orderTypes
1 = {'Not Premium':'False'} #not Premium
2 = {'Premium':'True'} #Premium
Assume I have a variable order_price = 5 #£5
How can I
Create an order,
If order is premium, then set order_price to 10 #order_price * 2
If order is NOT premium, then set order_price to 5
Create an instance of OrderPricing, that's related to the order. Also, pass the order_price variable to the property TotalPrice when creating the instance
from what I have seen and tried, I can override the Create() on the serializer as such
class OrderSerializer(WritableNestedModelSerializer):
"""OrderSerializer"""
# orderPricing = OrderPricingSerializer(required=False)
class Meta:
model = Order
fields = ('__all__')
def create(self, validated_data):
#create instance of order
#determine of order is premium
typeid = uuid.UUID(validated_data.pop('orderType'))#get FK value
isPremium = OrderType.objects.get(id = str(typeid.id))#determine if **Premium** is True/False
# set/calculate the price of the order
#create a related instance of OrderPricing
Q2
I am aware of GenericViews and the CreateModelMixin, what I don't know is, which is better, overriding the .create() at the serializer or overriding the CreateModelMixin method at the GenericView
Well, where to put business logic is always question hard to answer.
You have multiple places where it can be - view, serializer, model or some other separate module/service.
All have pros and cons- you can find many articles on this topic.
But in your case, I would probably go with perform_create of your view and I would create a method in the serializer which would update the price. If I needed to use the code to update price, I'd move to separate shared module and call it from there.
So let's say you use CreateModelMixin or better ListCreateAPIView
class YourView(ListCreateAPIView):
serializer = OrderSerializer
queryset = your_queryset
def perform_create(self, serializer):
serializer.update_price()
serializer.save()
perform_create is called after data is validated, so you can access the validated data.
update_price is your code where you update the price.
You can argue to move this logic to serializer's create or save method but they do many other things, so unless you need to override these methods for other reasons - you can take advantage of the perform_create method.

Create a serializer for custom user model in django-rest-auth?

I've installed django-rest-framework. Also i've installed django allauth.
Now i want to use django-rest-auth based on previous two, for serializing data
and sending it in json format as answer on my submit form method (for registration/login etc).
I look up into documentation is provided for django-rest-auth
but it feels for me strange, as i inherit(and must do so) from the AbstractBaseUser and not simply do OneToOneField on existing User model.
At the moment the model i assign to the AUTH_USER_MODEL in my settings.py is:
class Account(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, db_index=True, verbose_name='Account Email')
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_employer = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
The question is: how can i use serializing in rest-auth the same way, but using my model instead of provided UserProfile model example ?
Yes you may inherit it, and use serializers the way you are saying. It should work. Please state where the error arises when you are using serializers for this.

Cannot deserialize object in django rest framework

I'm closely reading http://www.django-rest-framework.org/api-guide/serializers and attempting to implement a basic deserializer. I'm somewhat perplexed as my experimental code is resulting in random results which make little sense.
I have a simple django model:
class ArticleType(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=15)
class Article(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=80)
body = models.TextField()
featured = models.BooleanField(default=False)
published = models.BooleanField(default=False)
children = models.ManyToManyField('self')
article_type = models.ForeignKey(ArticleType)
And I have a simple deserializer:
class ArticleSerializer(ModelSerializer):
article_type = serializers.CharField(max_length=15)
children = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Article
fields = ('id', 'featured','published','body','title','children','article_type')
In the django shell I run the following:
>>> aData = {'id':3,'featured':True,'published':True, 'body':'This is some body text!', 'title':'This is a title!', 'children':[2,3], 'article_type':'Topic'}
>>> aS = ArticleSerializer(data=aData)
Which yields:
>>> aS.is_valid()
True
>>> aS.data
{'featured': False, 'published': False, 'body': u'', 'title': u'', 'children': [], 'a_type': u''}
From this I have several questions.
Why has the data been changed?
If the data is invalid, why is the .is_valid() method returning true?
The documentation is vague on where I am to implement the transformation from CharField with length 15 to actually return the ArticleType instance in my article_type field.
NOTE: The article table is populated with a few dummy articles.
The data hasn't been changed. Those are your defaults as specified in the model. It's your serializer definition that causes the .is_valid to return true.
I think you want to define it like this:
class ArticleTypeSerializer(serializers.ModelSerializer):
model = models.ArticleType
fields= ('id', 'name',)
class ArticleSerializer(serializers.ModelSerializer):
article_type = ArticleTypeSerializer
class Meta:
model = models.Article
fields = ('id', 'featured','published','body','title','children','article_type',)
Also, I'm pretty sure that you want to add blank=True to your definition of the children in the model as otherwise every article has children, which have children, which have children, ad infinitum.
One thing I'm not sure about is what the behaviour should be when deserializing M2M fields. Normally django requires you to save the (in your case) Article and then add the children. The serializer doesn't actually do the saving so it wont be able to populate the children. That is the topic for another question however.

Resources