How to design a Django API to handle a "Dynamic" form? - django

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

Related

How can I access a value from one model and pass to another model in Django?

I have one model called Weight (filled by User input/choice) and another called Enterprise.
class Weight(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="weights")
weight_of_history = models.IntegerField(null=True, blank=True)
class Enterprise(models.Model):
...
The weight is saved, one per user, and replaced everytime the user choose new one.
Inside the Enterprise class, I am creating an property that must get the "weight_of_history" (depending on the user, who has chosen the weight) from Weight class, but the models have no Foreign key or related name between them.
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = <-- HERE I NEED TO TAKE WEIGHT_HISTORY FROM THE FIRST MODEL
THEN I COULD CALCULATE
How could I do that? Thank you!
You can use django's powerful query functionality and fetch the required objects from the db. Here are the docs that might help you with that. Django docs are amazing, so I would recommend that you read up on queries, models, and forms to have easier time with you project.
For your example we can fetch all the weights for the user in one query by filtering the weights by user. In fact, django ORM allows for chaining filters and you can create really sophisticated queries
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = Weight.objects.filter(user=some_user)
If you do not know the user beforehand, then you can do inside the view and grab the user that makes the request and filter using this user:
#views.py
user_making_request = request.user
weight_of_history = Weight.objects.filter(user=user_making_request)

How to handle calculated models in django?

From a model called Suggestion, I need to calculate another model that is called Task.
Task objects doesn't need to store anything in the database since it is totally calculated using Suggestion fields.
An exemple would be a task of approving a suggestion using fields :
approver
state
approving_deadline
Is their a standard way in django to handle this kind of models that doesn't need to store any information but are fully calculated using other models ?
You should use a SerializerMethodField:
In your Serializer, create a field for your Task like task = serializers.SerializerMethodField()
Still, in your serializer, create a method called get_taks(self, obj). obj is your current Suggestion, from which you need to calculate your task and return it as serialized data (the Task primary key using task.pk, or a serialized Task using TaskSerializer(task).data, or whatever you prefer).
ex.:
class SuggestionSerializer(serializers.ModelSerializer):
task = serializers.SerializerMethodField()
class Meta:
model = Suggestion
fields = [
'id', 'approver', 'state', 'approving_deadline',
..., 'task'
]
def get_tasks(self, obj):
# obj is a Suggestion instance
task = my_get_task_from_suggestion_helper(obj)
return task.id # or TaskSeriaizer(task).data
You can read more detail about SerializerMethodField on Django REST Framework docs

How can I update two models in one serializer in Django Rest Framework?

I have a database schema that has each object of a certain type being stored across two separate tables (one row in each table, different data in each, with a foreign key from one to the other.)
Unfortunately, Django Rest Framework tends to assume that there is a one to one correspondence between serializers and models, which is not true of my case. How should I be approaching this? It seems like the serializer should return the representation of the object which will be the actual HTTP response of the ajax requests, so using two serializers doesn't seem right. I've looked at extending BaseSerializer (which is how I currently plan to implement this if I don't find better solutions), but certain methods take in an instance, which should contain all the data needed to serialize the object, whereas I have two instances relevant.
Any advice would be super appreciated! Thank you.
Writable nested representations section might help you.
You have 2 models ModelA and ModelB. Create your first model's serializer
class ModelASerializer(serializers.ModelSerializer):
class Meta:
model = ModelA
fields = ('fields',..) #
Then in other model's serializer add the first serializer and override the required methods (like create, update). Something like this:
class ModelBSerializer(serializers.ModelSerializer):
# add the serializer for the foreignkey model
model_a = ModelASerializer()
class Meta:
model = ModelB
fields = ('fields',..) #
def create(self, validated_data):
modela_data = validated_data.pop('model_a')
model_b = ModelB.objects.create(**validated_data)
ModelA.objects.create(model_b=model_b, **modela_data)
return model_b
# override update too ..

Django admin: don't send all options for a field?

One of my Django admin "edit object" pages started loading very slowly because of a ForeignKey on another object there that has a lot of instances. Is there a way I could tell Django to render the field, but not send any options, because I'm going to pull them via AJAX based on a choice in another SelectBox?
You can set the queryset of that ModelChoiceField to empty in your ModelForm.
class MyAdminForm(forms.ModelForm):
def __init__(self):
self.fields['MY_MODEL_CHOIE_FIELD'].queryset = RelatedModel.objects.empty()
class Meta:
model = MyModel
fields = [...]
I think you can try raw_id_fields
By default, Django’s admin uses a select-box interface () for fields that are ForeignKey. Sometimes you don’t want to incur the overhead of having to select all the related instances to display in the drop-down.
raw_id_fields is a list of fields you would like to change into an Input widget for either a ForeignKey or ManyToManyField
Or you need to create a custom admin form
MY_CHOICES = (
('', '---------'),
)
class MyAdminForm(forms.ModelForm):
my_field = forms.ChoiceField(choices=MY_CHOICES)
class Meta:
model = MyModel
fields = [...]
class MyAdmin(admin.ModelAdmin):
form = MyAdminForm
Neither of the other answers worked for me, so I read Django's internals and tried on my own:
class EmptySelectWidget(Select):
"""
A class that behaves like Select from django.forms.widgets, but doesn't
display any options other than the empty and selected ones. The remaining
ones can be pulled via AJAX in order to perform chaining and save
bandwidth and time on page generation.
To use it, specify the widget as described here in "Overriding the
default fields":
https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/
This class is related to the following StackOverflow problem:
> One of my Django admin "edit object" pages started loading very slowly
> because of a ForeignKey on another object there that has a lot of
> instances. Is there a way I could tell Django to render the field, but
> not send any options, because I'm going to pull them via AJAX based on
> a choice in another SelectBox?
Source: http://stackoverflow.com/q/37327422/1091116
"""
def render_options(self, *args, **kwargs):
# copy the choices so that we don't risk affecting validation by
# references (I hadn't checked if this works without this trick)
choices_copy = self.choices
self.choices = [('', '---------'), ]
ret = super(EmptySelectWidget, self).render_options(*args, **kwargs)
self.choices = choices_copy
return ret

Inline formset for creating new objects with one-to-many relationship?

models.py:
class Item(models.Model):
name = models.CharField(max_length=50)
class ItemImage(models.Model):
image = models.ImageField(upload_to='item_pics')
item = models.ForeignKey(Item, related_name='images')
With the above defined models, Item has a one-to-many relationship to ItemImage. I am working on a django form that enables user to create a new Item instance, along with the choice of uploading up to two ItemImage. Note that the Item and the associating ItemImage objects need to be created at the same stage.
forms.py:
class ItemForm(ModelForm):
img_1 = fields.ImageField()
img_2 = fields.ImageField()
class Meta:
model = Item
fields = ('name', 'description', 'circles', 'location', 'rental_fee', 'rental_time_unit', 'deposit', 'rental_rules',)
On a second thought, instead of adding the image fields to ItemForm, I am guessing it might be a better approach to create a ModelForm for ItemImage and use inline formset. I have never used inline formset and thus my concept of it is still vague. I want to know if this is a right scenario to use inline formset.
Yes, this would be a very typical situation in which you would use a ModelFormSet.
The approach used in your forms.py would be appropriate if img_1 and img_2 were actual fields of class Item, but a ModelFormSet is a better choice when dealing with a set of related models.
Consider the future case where you wish to allow users three images rather than two. Using a ModelFormSet, this would be a one character code alteration. If you want to collect ancillary information on the ItemImage class, this would also be a trivial change.