I have two models, one is pointing to other model.
class ModelA(models.Model):
field1 = models.CharField()
field2 = models.CharField()
class ModelB(models.Model):
field3 = models.CharField()
field4 = models.CharField()
modela = models.ForeignKey(ModelA)
I am using Django REST for data manipulation. I am trying to insert data to ModelA and ModelB using one ajax call. For one entry in ModelA, I have a list of entries in ModelB. So my ajax data is,
ajax_data = {modela: {....}, modelb: [{...}, {...}, ...]}
When I am using only one model, I am able to save, otherwise I am not. Can I save two related model in one one POST request?
Yes. I'm going to use the non-accepted answer from this question.
The Django docs state that you need to save the dependent model (A, in your case), before you can save the related model (B, in your case.)
Note that you must save an object before it can be assigned to a
foreign key relationship. For example, creating an Article with
unsaved Reporter raises ValueError:
>>> r3 = Reporter(first_name='John', last_name='Smith', email='john#example.com')
>>> Article.objects.create(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r3)
Traceback (most recent call last):
...
ValueError: save() prohibited to prevent data loss due to unsaved related object 'reporter'.
As the other answer says - you can either do this in two POSTs or handle it in the view. It's hard to give specifics without seeing your view code, but the DRF docs have this covered.
I've adapted Robert's code for use here. If it was an accepted answer, I'd just have linked it as a duplicate.
class ASerializer(serializers.ModelSerializer):
class Meta:
model = ModelA
fields = ('field1', 'field1')
class BSerializer(serializers.ModelSerializer):
bclass = ASerializer()
class Meta:
model = ModelB
fields = ('field1', 'field2', 'etc')
views.py
import generics
class BCreateAPIView(generics.CreateAPIView):
model = ModelB
serializer_class = BSerializer
Related
class question(models.Model):
question_id = models.IntegerField()
choice = models.CharField(max_length=100)
tags = models.ArrayField(models.CharField(max_length=100))
ERROR:
packages\djongo\models\fields.py", line 116, in _validate_container
for field in self.model_container._meta._get_fields(reverse=False):
AttributeError: 'CharField' object has no attribute '_meta'
I want to add a ArrayField in my models using djongo models. i hope 'tags' looks like ['tag1', 'tag2', 'tag3' ...]. But djongo requeirs model_container, but i only want an array contains strings, not a model ,please help~~
I was experiencing a similar situation: wanted to create an IntegerField array. I solved it with this workaround:
Create an intermediate model which stores your field.
For example, for my use case (with integers that represent quantity by each second) I created a model called Second which holds an attribute called data of type models.IntegerField():
class Second(models.Model):
data = models.IntegerField(null=False)
class Meta:
abstract = True
class ParentModel(models.Model):
_id = models.ObjectIdField()
by_second = models.ArrayField(
model_container=Second
)
objects = models.DjongoManager()
So I guess in your case you could use the same but changing models.IntegerField()by models.CharField(max_length=100)
I know it has been a long time since the question was asked, but I hope this helps someone having this issue :)
I have three models in a django DRF project:
class ModelA(models.Model):
name = ....
other fields...
class ModelB(models.Model):
name = ....
other fields...
class ModelC(models.Model):
name = ....
model_a = FKField(ModelA)
model_b = FKField(ModelB)
I was using the default ModelViewSet serializers for each model.
On my react frontend, I'm displaying a table containing 100 objects of ModelC. The request took 300ms. The problem is that instead of displaying just the pk id of modelA and ModelB in my table, I want to display their names. I've tried the following ways to get that data when I use the list() method of the viewset (retreive all modelc objects), but it significantly increases call times:
Serializing the fields in ModelCSerializer
class ModelCSerializer(serializers.ModelSerializer):
model_a = ModelASerializer(read_only=True)
model_b = ModelBSerializer(read_only=True)
class Meta:
model = ModelC
fields = '__all__'
Creating a new serializer to only return the name of the FK object
class ModelCSerializer(serializers.ModelSerializer):
model_a = ModelANameSerializer(read_only=True) (serializer only returns id and name)
model_b = ModelBNameSerializer(read_only=True) (serializer only returns id and name)
class Meta:
model = ModelC
fields = '__all__'
StringRelatedField
class ModelCSerializer(serializers.ModelSerializer):
model_a = serializer.StringRelatedField()
model_b = serializer.StringRelatedField()
class Meta:
model = ModelC
fields = '__all__'
Every way returns the data I need (except number 3 takes more work to get the FKobject's id) but now my table request takes 5.5 seconds. Is there a way to do this without significantly increasing call times? I guess this is due to the DB looking up 3 objects for every object I retrieve.
Also I wouldn't be able to make the primary_key of ModelA & ModelB the name field because they aren't unique.
Thanks
EDIT Answer for my example thanks to bdbd below:
class ModelCViewSet(viewsets.ModelViewSet):
queryset = ModelC.objects.select_related('model_a', 'model_b').all()
# ...
You can use select_related for this to optimise your queries and make sure that every object in your ModelC does not do extra DB hits
Say I have a serializer A
class SerializerA(ModelSerializer):
some_field = CharField()
some_other_field = CharField()
field_require_other_model = SerializerMethodField()
class Meta:
model = ModelA
fields = ('some_field', 'some_other_field', 'field_require_other_model')
def get_field_require_other_model(self, instance):
other_model_qs = ModelB.objects.filter(email=instance.email)
# say I want to get whatever that comes first
return other_model_qs.first().useful_info
As seen above, SerializerA uses ModelA for getting all the fields except that one in ModelB. I can get the info from ModelB doing what I did, but I don't know if this is the best way getting the data. I'm not sure if I need to hit database so many times or if there's a way to lazily evaluate it.
Also, what if I have another SerializerMethodField() that utilizes ModelB but for different info. Is this way still the best way to get the data?
How about using .annotate, annotating the other field onto modelA from modelB and then defining it as a charfield(or whatever the type is) on the serializer?
Something like
queryset = ModelA.objects.all().annotate(other_field_on_model_b=F('ModelB__other_field_on_model_b'))
then in the seralizer
class SerializerA(ModelSerializer):
some_field = CharField()
some_other_field = CharField()
other_field_on_model_b = CharField(required=False) #or whatever the field type is.
class Meta:
model = ModelA
fields = ('some_field', 'some_other_field', 'other_field_on_model_b')
Could do the annotation in get_queryset() or in the end point itself.
I need to be able to do a post on an api endpoint to save an adgroup model.The model has a many to many field. I know I need to overwrite the create() method.But How is where I am stuck at . The incoming request data will have the id for the other model (creative). This id will already be present in the creative table.
Django creates another table called adgroup_creative to hold this M2M relationship.I need to populate that table when saving this adgroup object.
class AdGroup(models.Model):
adgroup_name = models.CharField(max_length=200, verbose_name="Name")
creative = models.ManyToManyField(Creative, verbose_name="Creative")
class Creative(models.Model):
creative_name= models.CharField(max_length=200, verbose_name="Name", default=0)
ad_type= models.PositiveIntegerField(max_length=1,verbose_name="Ad Type")
class AdGroupSerializer(serializers.ModelSerializer):
class Meta:
model = AdGroup
fields = ('id','adgroup_name','creative')
class CreativeSerializer(serializers.ModelSerializer):
class Meta:
model = Creative
fields = ('id','creative_name')
class AdGroupViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = AdGroup.objects.all().order_by('-id')
serializer_class = AdGroupSerializer
https://codereview.stackexchange.com/questions/46160/django-rest-framework-add-remove-to-a-list
Save a many-to-many model in Django/REST?
You should have a look at the serializer relation documentation.
You don't need anything special if you simply use ID to represent a M2M relation with DRF. You'll need to override the create/update methods only if you intend to provide non existing related objects or use nested serializers.
In the current case, you don't need nested serializers because you want to provide related instances' IDs.
views.py
I'm creating a queryset that I want to serialize and return as JSON. The queryset looks like this:
all_objects = Program.objects.all()
test_data = serializers.serialize("json", all_objects, use_natural_keys=True)
This pulls back everything except for the 'User' model (which is linked across two models).
models.py
from django.db import models
from django.contrib.auth.models import User
class Time(models.Model):
user = models.ForeignKey(User)
...
class CostCode(models.Model):
program_name = models.TextField()
...
class Program(models.Model):
time = models.ForeignKey(Time)
program_select = models.ForeignKey(CostCode)
...
Question
My returned data has Time, Program, and CostCode information, but I'm unable to query back the 'User' table. How can I get back say the 'username' (from User Table) in the same queryset?
Note: I've changed my queryset to all_objects = Time.objects.all() and this gets User info, but then it doesn't pull in 'CostCode'. My models also have ModelManagers that return the get_by_natural_key so the relevant fields appear in my JSON.
Ultimately, I want data from all four models to appear in my serialized JSON fields, I'm just missing 'username'.
Here's a picture of how the JSON object currently appears in Firebug:
Thanks for any help!
It seems a bit heavyweight at first glance but you could look at using Django REST Framework:
http://www.django-rest-framework.org/api-guide/serializers#modelserializer
You can define and use the serializer classes without having to do anything else with the framework. The serializer returns a python dict which can then be easily dumped to JSON.
To get all fields from each related model as nested dicts you could do:
class ProgramSerializer(serializers.ModelSerializer):
class Meta:
model = Program
depth = 2
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
To customise which fields are included for each model you will need to define a ModelSerializer class for each of your models, for example to output only the username for the time.user:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', )
class TimeSerializer(serializers.ModelSerializer):
"""
specifying the field here rather than relying on `depth` to automatically
render nested relations allows us to specify a custom serializer class
"""
user = UserSerializer()
class Meta:
model = Time
class ProgramSerializer(serializers.ModelSerializer):
time = TimeSerializer()
class Meta:
model = Program
depth = 1 # render nested CostCode with default output
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
What you really want is a "deep" serialization of objects which Django does not natively support. This is a common problem, and it is discussed in detail here: Serializing Foreign Key objects in Django. See that question for some alternatives.
Normally Django expects you to serialize the Time, CostCode, Program, and User objects separately (i.e. a separate JSON array for each) and to refer to them by IDs. The IDs can either be the numeric primary keys (PKs) or a "natural" key defined with natural_key.
You could use natural_key to return any fields you want, including user.username. Alternatively, you could define a custom serializer output whatever you want there. Either of these approaches will probably make it impossible to load the data back into a Django database, which may not be a problem for you.