Pass data as list of dictionaries to Django Formset? - django

I have a model called Reporter and a straight-forward ReporterForm and a formset based on it called ReporterFormset as below:
class Reporter(models.Model):
name = models.CharField(max_length=100)
class ReporterForm(forms.ModelForm):
class Meta:
model = Reporter
fields = '__all__'
ReporterFormset = modelformset_factory(Reporter, form=ReporterForm, extra=1, can_delete=True)
I want to save new data coming as list of dictionaries like:
data = [
{'name': 'x'},
{'name': 'y'}
]
So, I tried to do:
reporters_formset = ReporterFormset(initial=data)
But it shows that the formset is not valid after executing reporters_formset.is_valid() and with empty reporters_formset.errors.
How can I achieve such?

Related

Nested Serializer in Django Rest Framework

I am trying to make a nested serializer but when I run the following code it gives me an empty list. I tried to replicate the solution of this question and my problem is exactly similar
The only difference is in that answer serializer.Serializer is used but I am using Model Serializer
class hhhSerializer(serializers.Modelserializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(read_only=True)
class Meta:
model = ItemBatch
fields = ('id','name')
class dispatchhistorySerializer(serializers.ModelSerializer):
truck_name = ReadOnlyField(source='truck_name.name')
truck_type = ReadOnlyField(source='truck_type.name')
items = hhhSerializer(many=True)
class Meta:
model = DispatchPlan
fields = "__all__"
Output:
"id": 35,
"truck_name": "24 ft mxl 14 Ton",
"truck_type": "Container",
"items": [
{},
{},
{},
{},
{},
{},
{},
{},
{},
{}
],
You have to declare the field explicitly at DispatchHistorySerializer.Meta.fields; now, as personal recommendation avoid always "all" in the field list
This code should work (I had renamed your classes to comform python convention)
class HhhSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(read_only=True)
class Meta:
model = ItemBatch
fields = ('id','name')
class DispatchHistorySerializer(serializers.ModelSerializer):
truck_name = ReadOnlyField(source='truck_name.name')
truck_type = ReadOnlyField(source='truck_type.name')
items = HhhSerializer(many=True) # 2) here we say how to serialize 'items'
class Meta:
model = DispatchPlan
fields = ('id', 'truck_name', 'truck_type', 'items',) # 1) here we say: include 'items' please
EDIT: if using ModelSerializer, define which model in the Meta class; if it isn't a ModelSerializer, use a simple Serializer instead

Django Rest Framework: How to pass a list of UUIDs for a nested relationship to a serializer?

TL;DR: What could be the reason the incoming data for one of my serializers does not get processed?
I'm working on a serializer for a nested relationship. The serializer should get a list of UUIDs, so that I can make many to many relationships. Here is the model:
class Order(
UniversallyUniqueIdentifiable,
SoftDeletableModel,
TimeStampedModel,
models.Model
):
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
custom_choice_items = models.ManyToManyField(CustomChoiceItem, blank=True)
price = models.ForeignKey(MenuItemPrice, on_delete=models.CASCADE)
amount = models.PositiveSmallIntegerField(
validators=[MinValueValidator(MINIMUM_ORDER_AMOUNT)]
)
Here is the data (my post body) with which I hit the route in my tests:
data = {
"checkin_uuid": self.checkin.uuid,
"custom_choice_items": [],
"menu_item": self.menu_item.uuid,
"price": self.menu_item_price.uuid,
"amount": ORDER_AMOUNT,
}
response = self.client.post(self.order_consumer_create_url, self.data)
Note that the empty list for custom_choice_items does not change anything. Even if I fill it with values the same error occurs. And last but not least here are the serializers:
class CustomChoiceItemUUIDSerializer(serializers.ModelSerializer):
"""Serializer just for the uuids, which is used when creating orders."""
class Meta:
model = CustomChoiceItem
fields = ["uuid"]
....
# The serializer that does not work
class OrderSerializer(serializers.ModelSerializer):
menu_item = serializers.UUIDField(source="menu_item.uuid")
custom_choice_items = CustomChoiceItemUUIDSerializer()
price = serializers.UUIDField(source="price.uuid")
wish = serializers.CharField(required=False)
class Meta:
model = Order
fields = [
"uuid",
"menu_item",
"custom_choice_items",
"price",
"amount",
"wish",
]
The problem is now, that when I leave out many=True, I get the error:
{'custom_choice_items': [ErrorDetail(string='This field is required.', code='required')]}
And If I set many=True I just simply don't get any data. By that I mean e.g. the value of validated_data["custom_choice_items"] in the serializers create() method is just empty.
What goes wrong here?
I even checked that the data is in the request self.context["request"].data includes a key custom_choice_items the way I pass the data to this view!
EDIT: Here is the data I pass to custom_choice_items:
data = {
“checkin_uuid”: self.checkin.uuid,
“custom_choice_items”: [{“uuid”: custom_choice_item.uuid}],
“menu_item”: self.menu_item.uuid,
“price”: self.menu_item_price.uuid,
“amount”: ORDER_AMOUNT,
}
self.client.credentials(HTTP_AUTHORIZATION=“Token ” + self.token.key)
response = self.client.post(self.order_consumer_create_url, data)
When you post data using the test api client, if the data contains nested structure you should use format=json, like this:
response = self.client.post(self.order_consumer_create_url, data, format='json')
Did you override .create method in the serializer? Something like this should work:
from django.db import transaction
class OrderSerializer(serializers.ModelSerializer):
# your fields and Meta class here
#transaction.atomic
def create(self, validated_data):
custom_choice_items = validated_data.pop('custom_choice_items')
order = super().create(validated_data)
order.custom_choice_items.add(*custom_choice_items)
return order
By the way you don't really need to define CustomChoiceItemUUIDSerializer if is just the primary key of that.

How to show objects in the form field (Multiple Select Field) based on a condition in django form.

I have a many to many field linked with my model1. Now, I created a form for this model1 and added this many to many field as a form field and used FilteredSelectMultiple widget to edit this. Now, the problem is the related many to many field has a soft delete option, which I am tracking with active field in the model2. So, now in the form all the objects are displayed even if they are deleted, is there any way I can show the objects which have active as true in this form field.
My model form looks as follows:
class Editform(form.ModelForm):
class Media:
css = ..
js = ..
class Meta:
Model = model1
fields = [ "x", "y", "ManytoManyfield"]
widgets = {
'ManytoManyfield': FilteredSelectMultiple("Displaay name", False)
}
This answer is close to what you want. I think this may work.
You create an extra field in your ModelForm, populating it with a query.
class Editform(form.ModelForm):
many_to_many_field_active = forms.ChoiceField(choices=[(m2m.id, m2m.name) for m2m in Model2.objects.filter(active=True)])
class Meta:
#...
widgets = {
'many_to_many_field_active': Select(attrs={'class': 'select'}),
I solved this using the multiplechoicefield in my model form as follows.
def __init__(self, *args, **kwargs):
many_to_m_initial = kwargs['instance'].model2.all()
choices = [(m2m.id, m2m.name) for m2m in Model2.objects.filter(active=True)]
self.fields['my_field'] = forms.MultipleChoiceField(choices = choices, widget=FilteredSelectMultiple("verbose name", is_stacked=False, choices=choices))
self.initial['my_field'] = [ m2m.pk fpr m2m in many_to_m_initial ]

Django REST framework: create JSON fragments from string

I want to store JSON fragments in TextField of my model with JSON:
class A(models.Model):
name = models.CharField(max_length=200)
people = models.TextField()
I have serializer class:
class ASerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = A
fields = ('name', 'people')
How can I told Django REST Framework to treat people string like JSON, not like string. E.g. when people is [ {"name":"A", "surname":"B"}] I want have in JSON generated by Django REST framework
"people" : [ {"name":"A", "surname":"B"}]
and not
"people" : "[ {\"name\":\"A\", \"surname\":\"B\"}]"
Edit: I change ASerializer class and used JSONField from django-jsonfield and everything works. New code below, transform_people method serves to serialization and validate_people to deserialization:
class ASerializer(serializers.ModelSerializer):
def transform_people(self, obj, value):
if obj is None:
return obj
else:
return obj.people
def validate_people(self, attrs, source):
return attrs
class Meta:
model = A
Convert your response to json object
import json
## In this case lets say
response = [{"name":"A", "surname":"B"}]
data = json.dumps(response)
print data
You could use serializers.JSONField
class ASerializer(serializers.HyperlinkedModelSerializer):
people = serializers.JSONField()
class Meta:
model = A
fields = ('name', 'people')

formset_factory with forms with different initial data

I am writing a view that uses POST data to display multiple forms with differing prefilled FK's
I have a ModelForm in forms.py
class SurveyForm(forms.ModelForm):
class Meta:
model = Survey
who's model looks like this...
class Survey(models.Model):
student = models.ForeignKey(Student)
surveyset = models.ForeignKey(SurveySet)
cei_0 = models.BooleanField()
cei_1 = models.BooleanField()
My view looks kind of like this so far
# ... after building a list from POST we essentially have:
list_of_studentids = [1,3,2,6,7,45]
students = []
for i in list_of_student_ids:
students.append(Student.objects.filter(id=i))
SurveyFormSet = formset_factory(SurveyForm, extra=6)
formset = SurveyFormSet(initial=[
{'surveyset': SurveySet.create(),
'student': ?????,}
])
How do I return a bunch of forms with different student FK's and the same surveyset FK?
You need to pass an instance attribute to the form:
prefilled_survey = Survey(student=student_instance, surveyset=surveyset_instance)
form = SurveyForm(request.POST or None, instance=prefilled_survey)