I have the following model:
class Item(models.Model):
itemID = models.IntegerField(unique=True)
url = models.CharField(max_length=300)
name = models.CharField(max_length=300)
price = models.FloatField()
and the following Serializer:
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model= Item
fields = ('id','itemID','url','name','price')
When posting an item that already exists I get the following message:
{"itemID":["item with this itemID already exists."]}
Instead, however, I would like to update all other fields if the item already exists.
It'd be best if you could include your view in your original post. That being said, model views will usually behave like so:
Assuming we have an Item view located at api/item/
To create an item, you POST at api/item/. It is mapped to the create method
To update an item, you PATCH or PUT at api/item/[item_id]/. It is mapped to the update method
To fetch an item, you GET at api/item/[item_id]/. It is mapped to the partial_update method
To delete an item, you DELETE at api/item/[item_id]/. It is mapped to the destroy method
To list all items, you GET at api/item/. It is mapped to the list method
Those methods and urls are automatically available when using the ModelViewSet from Django Rest Framework, though you could override them and make them your own.
When using a ModelSerializer, you have 2 choices when initializing the serializer:
serializer(data=data) will end up creating a new model instance on save
serializer(instance, data=data) will allow you to update an existing instance when saving it
Related
I have built an Angular form that contains a form array. So the user has the control to add fields or delete fields from the form. What I am trying to understand is how I can design a Django API that can handle the post for this kind of dynamic form?
I do not want the user to create an account on the website in order to place his order. Is that even possible?
You should be more concerned about how to model your data, than you can think about your interface. Here a few options for modeling your data:
Option One is to use regular Django ORM, and in this case you may use the JSONField for any dynamic properties.
Option two is to use any schemaless data model, such as document-based databases(e.g MongoDB).
Here a simple example, on how to use Django's JSONField:
your model:
class Order(models.Model):
customer = models.ForeignKey(User, on_delete=models.CASCADE)
#any additional static fields
properties = JSONField()
your view:
def create_order_view(request):
if request.method == "POST":
#do your validation
Order.objects.create(user=request.user, properties=request.POST["properties"])
return Response(status=200)
this example is totally incomplete as you have to add validation error handling, and it is a better idea to use Django rest-framework for constructing your API.
Finally as I said there many option to model your data, in addition to what I mentioned above there are many other. To decide what model to use, you have to know how your data gonna be consumed, so you can optimze for query time, in addition there are many other factors but this is out of scope of this asnwer.
For me, I used Django-RESTframework to build the api.
The way to achieve this is simple, just create the model and iterate through the items which is the dynamic part, and assign the Foreignkey field to obj.id created. First, I created the main model instance, then created the instances of the child instances. I will use Order and Item to demonstrate the idea, The Item instance will have Foreinkey field to Order model.
In the Item model, add "related_name" argument to the Foreinkey field
order = models.ForeignKey(Order, related_name='items',on_delete=models.CASCADE)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = [
....your fields...
]
class OrderSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True)
class Meta:
model = Order
fields = [
'order', ....
]
def create(self, validated_data):
items_data = validated_data.pop("items")
order = Order.objects.create(**validated_data)
order.total_fees = order.delivery_fees
for item in items_data:
i = Item.objects.create(order=order, **item)
return order
In django admin I have Model A with a foreign key association to Model B. Model B's values change based on the value of Model A.
When a Model B object is selected for association with a Model A object, I would like to immediately display updated values for Model B based on the current value of Model A.
I know that I can override the on_save method in the form to update the values when the user saves the form to the database. However, I would like the admin view to display the values before the user hits save.
What do I need to hook into to make this update happen?
Thank You
If you want to dinamically filter Model B values in the changeview during user interaction (that is: before submission), you need javascript:
1) after page rendering, attach a "change handler" to Model A input field
2) in that handler, call via Ajax a view to retrieve the list of values available for Model B according to the currently selected value of Model A
3) upon receiving the list, update the Model B input field accordingly
4) also, after the initial page rendering you should explicitly invoke the handler in order to have Model B input field correctly initialized
This should work for both "add" and "change" view.
I do believe that a very detailed tutorial on how to implement this procedure can be found here:
https://simpleisbetterthancomplex.com/tutorial/2018/01/29/how-to-implement-dependent-or-chained-dropdown-list-with-django.html
The example refers to a front-end view, but can be easily adapted to the admin changeview
Let's say here what you've got for models:
# Model B
class ModelB(models.Model):
pass
# Model A
class ModelA(models.Model):
b_link = models.ForeignKey(ModelB)
I assume that you don't want to use javascript to manipulate the form but parsing it from server. In that case, what you can do is just create a preview Model B, and create the ModelForm from this model.
For example:
class ModelB(models.Model):
...
# add a method to preview B - This will not save model
def preview_b(model_a):
# update values of b according to model_a
b.derived_value = model_a.value
# file: forms.py
class ModelBForm(ModelForm):
class Meta:
model = ModelB
# file: views.py
b_model = Model.objects.all().first()
b_model.preview_b(a_model)
form = ModelBForm(instance=b_model)
This, of course, requires you to post back to server whenever choosing a new ModelA but I think that was what you wanted.
I have an angular app that presents a form with contact data and a list of contact numbers. Each contact can have N numbers and each of those numbers has a type (cell,home,work...) The code below will send the json to the angular app just fine and I can deal with it there including adding new numbers, removing numbers ..... However when DRF gets the exact same format json back, it can't deserialize it. It throws off this error:
AttributeError: 'Contact' object has no attribute 'numbers'
which is totally valid, but the serializer DOES have that field and should be able to hang onto those values so I can save them after I save the contact.
If I do something totally hokey like this in the update method:
self.object = self.get_object_or_none()
self.object.numbers = []
I can eliminate that error, but then it throws off these kind of errors:
{'numbers': [
{u'non_field_errors': [u'Cannot create a new item, only existing items may be updated.']},
{u'non_field_errors': [u'Cannot create a new item, only existing items may be updated.']},
{u'non_field_errors': [u'Cannot create a new item, only existing items may be updated.']}
]}
The first two phone numbers aren't new, they have id fields and came from the db, the third one is new, I'm trying to add it.
Here is the code. Surely this isn't that bizarre a way to do things. Is Django Rest Framework what I should be using? I keep running into show stoppers like this that seem to be the documented way to do things, but then they blow up spectacularly.
class PhoneTypeSerializer(serializers.ModelSerializer):
class Meta:
model = PhoneType
class ContactPhoneSerializer(serializers.ModelSerializer):
number_type = PhoneTypeSerializer(source='number_type')
class Meta:
model = ContactPhone
depth = 1
exclude = ('owner',)
class ContactSerializer(serializers.ModelSerializer):
numbers = ContactPhoneSerializer(source='number_set', many=True, required=False)
class Meta:
model = Contact
How do I deserialize this data so I can save it?
I had the same issue and solved it by adding to the serializer some flags. In your case it should be something like:
number_type = PhoneTypeSerializer(source='number_type', many=True, required=False, allow_add_remove=True, read_only=False)
A bit late, but maybe it still helps. Found this suggestion here:
Updating m2m not possible when using serializers as fields
I have the following situation. I have three models, Post, User and Friends.
class User(models.Model):
name = models.CharField(max_length=100)
class Friend(models.Model):
user1 = models.ForeignKey(User,related_name='my_friends1')
user2 = models.ForeignKey(User,related_name='my_friends2')
class Post(models.Model):
subject = models.CharField(max_length=100)
user = models.ForeignKey(User)
Every time I bring users, I want to bring the number of his friends:
User.objects.filter(name__startswith='Joe').annotate(fc=Count('my_friends1'))
This works fine.
However, I want to make this work when I bring the users as nested objects of Post. I'm using there select_related to minimized DB calls, so I want to do something like:
Post.objects.filter(subject='sport').select_related('user').annotate(user__fc=Count('user__my_friends1'))
However, this creates field user__fc under post, and not field fc under post.user.
Is there a way to achieve this functionality?
You can make use of Prefetch class:
from django.db.models import Count, Prefetch
posts = Post.objects.all().prefetch_related(Prefetch('user', User.objects.annotate(fc=Count('my_friends1'))))
for post in posts:
print(post.subject)
print(post.user.fc)
NB : this does two database queries (Django does the join between Post and User in this case) :
'SELECT "myapp_post"."id", "myapp_post"."subject", "myapp_post"."user_id" FROM "myapp_post"
'SELECT "myapp_user"."id", "myapp_user"."password", "myapp_user"."last_login", "myapp_user"."is_superuser", "myapp_user"."username", "myapp_user"."first_name", "myapp_user"."last_name", "myapp_user"."email", "myapp_user"."is_staff", "myapp_user"."is_active", "myapp_user"."date_joined", COUNT("myapp_friend"."id") AS "fc" FROM "myapp_user" LEFT OUTER JOIN "myapp_friend" ON ("myapp_user"."id" = "myapp_friend"."user1_id") WHERE "myapp_user"."id" IN (3, 4) GROUP BY "myapp_user"."id", "myapp_user"."password", "myapp_user"."last_login", "myapp_user"."is_superuser", "myapp_user"."username", "myapp_user"."first_name", "myapp_user"."last_name", "myapp_user"."email", "myapp_user"."is_staff", "myapp_user"."is_active", "myapp_user"."date_joined"
You can define a custom manger for your models, as described here and then override its get_queryset() method to add the custom column to your model upon query.
In order to use this manager for a reverse relation, you should set the base manager as described in the docs.
Another approach would be something like this, which you specify the manager of the related model with a hard-coded attribute.
I would like to create a model where the user can add objects of a model to a list in Django.
For example:
class Receipt(models.Model):
items = #many to many maybe? but their is only one receipt for each item object
#other fields
class Item(models.Model):
name = model.CharField()
#otherfields
Class ItemPurchase(models.Model):
receipt = model.ForeignKey(Receipt)
item = model.ForeignKey(Item)
quantity = model.IntegerField()
#other fields specific to this Receipt
Then in the admin, I want to allow the user to add Item objects to their current Receipt objects in the admin panel, and create an ItemPurchase record, but how would I best do this? The Receipt cannot be associated until the Receipt is made...
Turns out Django's inline feature combined with custom queryset returns is exactly what I needed to do everything I wanted, thanks #Hedde for the tip!
Read about inlines:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#inlinemodeladmin-objects
This helped in multiple ways, I just needed a push in the right direction:
formfield_for_foreignkey and Inline Admin