Django Restframework N to N Table - python

I have two classes in my model.py:
class Table(models.Model):
name = models.CharField(max_length=100, blank=True)
class Activity(models.Model):
name = models.CharField(max_length=100, blank=False)
process = models.CharField(max_length=100, blank=True, default='')
For these two classes I created a serializer each:
class TableSerializer(serializers.Serializer):
pk = serializers.IntegerField(read_only=True)
name = serializers.CharField(required=True, allow_blank=False, max_length=100)
def create(self, validated_data):
return Table.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.save()
return instance
(same for Activity)
I now want to store the relation between table and activity. An activity can have multiple tables. Now I don't know if I need to create a new class like this:
class TableActivity(models.Model):
activtiy = models.ManyToManyField('Activity')
table = models.ManyToManyField('Table')
Or that this can be included into the activity already?
How should my model/serializer look like then?

You shouldn't create class TableActivity. You can add field activtiy = models.ManyToManyField('Activity') to table Table or add field table = models.ManyToManyField('Table') to table Activity. And you should use model serializer for creating object in database

Related

How to define a field of a model to have a choice from another field of another model of another app?

I have two models defined in two different apps.
I want to have a choice option for a particular field in a model, which is deriving those attributes from another field of a model in another app.
eg: I have a model "Inventory" that has a field 'title'.
class Inventory(models.Model):
title = models.CharField('Title', max_length=120, default='')
....
I also have another model 'Invoice' that has a field 'line_one'. Both these models are located in different apps.
class Invoice(models.Model):
line_one = models.CharField('Line 1', max_length=120)
....
I created a Django form for adding the Invoice details. I want a choice option for line_one that has all the values stored in the Inventory.title field so that the user can only select those values from a dropdown menu.
How can I implement this?
Thanks.
Edit: I referenced line_one to the field title of the Inventory model and it seems to work but it isn't exactly showing the name of the attributes but showing, "Inventory Object(7)", "Inventory Object(8)"
line_one = models.ForeignKey(Inventory, to_field='title', default=0, on_delete=models.CASCADE)
Use a CharField for line_one
class Inventory(models.Model):
title = models.CharField('Title', max_length=120, default='')
# ...
class Invoice(models.Model):
line_one = models.CharField('Line 1', max_length=120)
# ...
class InvoiceForm(forms.ModelForm):
line_one = forms.CharField(widget=forms.Select)
class Meta:
model = Invoice
fields = ['line_one', ] # ...
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# populate the choices from the Inventory model objects
self.fields['line_one'].widget.choices = [(i.title, i.title) for i in Inventory.objects.all()]
# or
self.fields['line_one'].widget.choices = [(t,t) for t in Inventory.objects.values_list('title', flat=True)
Use a ForeignKey for line_one
If you like to use a ForeignKey, it is necessary to have unique values for Inventory.title.
class Inventory(models.Model):
title = models.CharField('Title', max_length=120, default='')
# ...
# The __str()__ method of an object is used by django
# to generate the labels for a Select input widget
def __str__(self):
return self.title
class Invoice(models.Model):
line_one = models.ForeignKey(Inventory, to_field='title', on_delete=models.CASCADE)
# ...
class InvoiceForm(forms.ModelForm):
class Meta:
model = Invoice
fields = ['line_one', ] # ...
# no need for any form adjustments,
# as django uses a select input as default for ForeignKey fields

Django Rest Framework : Setting foreign key value in serializer

I have the following models:
class School(models.Model):
id = patch.BigAutoField(primary_key=True)
name = models.CharField('Name', max_length=100)
address = models.CharField('Address', max_length=500, blank=True, null=True)
class Child(BaseModel):
id = patch.BigAutoField(primary_key=True)
name = models.CharField('Name', max_length=100, blank=True, null=True)
school = models.ForeignKey('User', blank=True, null=True, db_constraint=False, db_index=True, on_delete=models.CASCADE, default=None)
I have the following serializers :
class SchoolSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(required=True, max_length=100)
address = serializers.CharField(required=False, max_length=400)
def create(self, validated_data):
return School.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.address = validated_data.get('address', instance.address)
instance.save()
return instance
class Meta:
model = School
fields = ('id', 'name', 'address')
class ChildSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(required=False, max_length=100, allow_blank=False)
school = SchoolSerializer()
def create(self, validated_data):
return Child.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.school = validated_data.get('school', instance.school)
instance.save()
return instance
Now the problem that I am facing is that when I saving any value in my child table using serializer then the value of school is showing null in my database but in my request object I am getting value for school_id.
Since this school = SchoolSerializer() school would be a fully serialized object, not scalar value.
Take a look at this example it should help: Writable nested serializer with existing objects using Django Rest Framework 3.2.2
You got to convert serialized object school manually into scalar valued school PK to fill school_id FK field. Or just remove school = SchoolSerializer(), then ChildSerializer will start serializing this field as scalar-valued (but still FK) and thus will make it simply writable directly to the school_id field. The rest of the code should work well.

Many to Many model with a dropdown in Django Rest Framework?

I am trying to create a Many to Many relation with a model in between, I have a Client model, and a Zone model, each client may have access to different zones, and each zone may have multiple clients.
Therefore I created a model called Access Permission, that stores said relation, and I want to show a dropdown selector in the post form that shows the existing clients and zones, or to ask for the Id of an existing object, instead of showing the form to create new ones.
These are my models:
class Zone(models.Model):
name = models.TextField()
created = models.DateTimeField(auto_now=True)
def __str__(self):
return '%s' % (self.name)
class Client(models.Model):
name = models.TextField()
birthDate = models.DateField()
created = models.DateTimeField(auto_now=True)
def __str__(self):
return '%s' % (self.name)
class AccessPermission(models.Model):
idClient = models.ForeignKey(Client, on_delete=models.CASCADE, null=False)
idZone = models.ForeignKey(Zone, on_delete=models.CASCADE, null=False)
And these my current serializers:
class ZoneSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Zone
fields = ('name',)
class ClientSerializer(serializers.HyperlinkedModelSerializer):
zones = ZonesSerializer(source='accesspermission_set', many=True, read_only=True)
class Meta:
model = Client
fields = ('name', 'birthDate', 'zones')
class AccessPermissionSerializer(serializers.ManyRelatedField):
idClient = ClientSerializer(many=False)
idZone = ZoneSerializer(many=False)
class Meta:
model = AccessPermission
fields = ('idClient', 'idZone')
Is there any way to ask for the Id of an existing object, or show the existing ones, instead of the fields to create new ones?
You can do it like:
models
class AccessPermission(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE, null=False)
zone = models.ForeignKey(Zone, on_delete=models.CASCADE, null=False)
serializers
class AccessPermissionSerializer(serializers.ManyRelatedField):
id = serializers.IntegerField(read_only=True)
client_id = serializers.PrimaryKeyRelatedField(
queryset=Client.objects.all(), source='client', allow_null=False, required=True
)
zone_id = serializers.PrimaryKeyRelatedField(
queryset=Zone.objects.all(), source='zone', allow_null=False, required=True
)
class Meta:
model = AccessPermission
fields = (
'id', 'client_id', 'zone_id'
)

Django many-to-many serialization

I want to create a model (Source) with many-to-many relation to the another model (Tag) and create a Source objects without duplicating Tag instance in database.
Here is my models:
class Tag(models.Model):
name = models.CharField(max_length=50, null=False, default='source')
def __unicode__(self):
return self.name
class Source(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
language = models.CharField(max_length=50)
color = models.CharField(max_length=50, default='white')
isFile = models.BooleanField(default=False)
link = models.TextField(default='')
file = models.FileField(upload_to='uploads/', null=True)
tags = models.ManyToManyField('Tag')
class Meta:
ordering = ('title',)
Here is my serializers:
class TagSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = Tag
fields = ('name',)
class SourceSerializers(serializers.ModelSerializer):
tags = TagSerializers(many=True)
class Meta:
model = Source
fields = ('title', 'author', 'language', 'color', 'isFile', 'link', 'file', 'tags')
def create(self, validated_data):
tags_data = validated_data.pop('tags')
source = Source.objects.create(**validated_data)
for tag in tags_data:
t = Tag.objects.create()
t.name = tag.get("name")
t.save()
source.tags.add(t)
source.save()
return source
But when I try to create Source object via http request - the object is created, but without any references to Tags. After some researching I found that validated_data in create(self, validated_data) doesn't contains "tags" field, also I found that validate function of TagSerializer not invoked at any time. What I'm doing wrong?
Use get_or_create method to create Tag object.
def create(self, validated_data):
tags_data = validated_data.pop('tags')
source = Source.objects.create(**validated_data)
for tag in tags_data:
name = tag.get("name")
t = Tag.objects.get_or_create(name=name)
t.save()
source.tags.add(t)
source.save()
return source
Seems the problem was in my requests, without many-to-many relation we can use form-data and all is good, but when we add mant-to-many relation we can't use form-data anymore and have to use only application\json

Where I can write business logic when I want to create other instance using Rest Framework?

Where I can write myself logic when I use Rest Framework?
I have a serializers:
class OrderSerializer(ModelSerializer):
order_num = serializers.SerializerMethodField()
price = serializers.SerializerMethodField()
class Meta:
model = Order
fields = ('name', 'room', 'price', 'order_num')
def get_order_num(self, obj):
return str(now()) + "111"
def get_price(self, obj):
print(obj, "liao obj")
return "0.10"
And I have a views:
class OrderCreateAPIView(CreateAPIView):
serializer_class = OrderSerializer
queryset = Order.objects.all()
The models is below:
class Room(models.Model):
name = models.CharField(max_length=12)
number = models.IntegerField()
class Order(models.Model):
name = models.CharField(max_length=12)
order_num = models.CharField(max_length=12)
price = models.CharField(max_length=12)
room = models.ForeignKey(to=Room, related_name="orders")
start_time = models.DateTimeField(null=True, blank=True)
end_time = models.DateTimeField(null=True, blank=True)
This demo is my test demo, don't care the details.
And my goal is want to create a instance of room when I access the view/serializer.
You see the snapshot, I can create the Order instance success, but my goal is when create a Order, I want to create a Room instance(don't care the room's initial data, just for test we can set it constant data). and return the room's id.
I don't know where to do to create Room instance logic. some friends can help me with that?
EDIT
in the django rest framework docs, I find I can override the create method to realize my requirement:
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
def create(self, validated_data):
tracks_data = validated_data.pop('tracks')
album = Album.objects.create(**validated_data)
for track_data in tracks_data:
Track.objects.create(album=album, **track_data)
return album
But, this is a aspect(means in serializer), how to realize in views.py ?

Categories

Resources