i'm recently moving my db to a model Inheritance structure. Here an example:
Task model
STATUS_CHOISE = (('PR', 'In process'), ('ST', 'Stopped'), ('FN', 'Finished'), ('DL', 'Deleted'),)
class Task(models.Model):
owner = models.ForeignKey(User)
process = models.ForeignKey(Process)
title = models.CharField(max_length=200, default='')
description = models.CharField(max_length=1000, default='')
date_created = models.TimeField(auto_now_add=True, auto_now=False)
date_deadline = models.DateTimeField(default=lambda: (datetime.now() + timedelta(days=7)), auto_now_add=False)
parameters = jsonfield.JSONField()
objects = InheritanceManager()
status = models.CharField(max_length=2, choices=STATUS_CHOISE, default='ST')
here the HumanTask that extends Task
PLATFORMS = (('CC', 'CrowdComputer'), ('MT', 'Amazon Mechancial Turk'),)
class HumanTask(Task):
number_of_instances = models.IntegerField(default=1)
uuid = models.CharField(max_length=36, default='')
page_url = models.URLField(max_length=400, default='', null=True, blank=True)
platform = models.CharField(max_length=2,choices=PLATFORMS, default='CroCo')
validation=models.OneToOneField(ValidationTask)
reward = models.OneToOneField(Reward, null=True, blank=True)
now, how should i create the Form? Should i use ModelForm for both classes?
The point is: there are fields that have to be exclude
for example, TaskForm is:
class TaskForm(ModelForm):
owner = forms.ModelChoiceField(queryset=User.objects.all(),widget=forms.HiddenInput)
process = forms.ModelChoiceField(queryset=Process.objects.all(),widget=forms.HiddenInput)
class Meta:
model = Task
exclude = ('date_deadline', 'date_created','parameters','status','objects')
so what i want for the HumanTaskForm is that the exclude are inherited from the TaskForm
i tried with this
class HumanTaskForm(TaskForm):
class Meta:
model= HumanTask
exclude = 'uuid'
but does not work.
Summing up: is this correct? should i use Inheritance for forms? and, how can i have excluded fields, and others parameters, Inheritance?
If you want to leverage the exclude from TaskForm in HumanTaskForm and extend it, you can inherit the Meta class from TaskForm:
class HumanTaskForm(TaskForm):
class Meta(TaskForm.Meta):
model = HumanTask
exclude = TaskForm.Meta.exclude + ('uuid',)
You need to inherit the parent Meta as well as.
The child class will inherit/copy the parent Meta class. Any attribute explicitly set in the child meta will override the inherited version. To my knowledge there is no way to extend the parent Meta attributes (ie adding to 'exclude').
class AwesomeForm(forms.ModelForm):
class Meta:
model = AwesomeModel
exclude = ('title', )
class BrilliantForm(AwesomeForm)
class Meta(AwesomeForm):
model = BrilliantModel
.
print(AwesomeForm.Meta.model)
> AwesomeModel
print(BrilliantForm.Meta.model)
> BrilliantModel
print(AwesomeForm.Meta.exclude)
> ('title', )
print(BrilliantForm.Meta.exclude)
> ('title', )
You could do something like this:
class BrilliantForm(AwesomeForm)
class Meta(AwesomeForm):
model = BrilliantModel
exclude = AwesomeForm.Meta.exclude + ('uuid', )
.
print(BrilliantForm.Meta.exclude)
> ('title', 'uuid')
Related
If I sound confused, it's because I am.
I'm unfamiliar with the django rest framework and I'm attempting to create a relatively simple Recipe-Managing app that allows you to automatically create your shopping list.
Motivations :
I know that DRF might not be needed and I could just use django, but the point of this app is to learn how to use the DRF.
The goal is to create a back with DRF and do some fancy shenanigans with a front framework afterward.
Problem:
I have a Recipe model which contains a ManyToMany field to Ingredient through RecipeIngredient. And I am a bit confused on how I should approach the RecipeSerializer.
So far it looks like that :
class RecipeSerializer(serializers.ModelSerializer):
class Meta:
model = Recipe
fields = ('id','name','ingredients','tags','prep_time','cook_time', 'servings', 'instructions')
But I feel like whenever I will want to create a Recipe, I'll have to fire a post request to create the Ingredients (if they do not exist yet), one to create the Instructions, one to create the Recipe and one to create the RecipeIngredients.
Question :
Is there a way to make one request containing the recipe and all sub fields (ingredient, recipeingredient, instruction) and to create all the entities ?
That would be handled by the create function of the RecipeSerializer I suppose.
Model:
class Tag(models.Model):
name = models.CharField(max_length=100, unique=True)
class Ingredient(models.Model):
name = models.CharField(max_length=100, unique=True)
class Recipe(models.Model):
name = models.CharField(max_length=100)
ingredients = models.ManyToManyField(Ingredient,through='RecipeIngredient')
tags = models.ManyToManyField(Tag, related_name='recipes')
prep_time = models.PositiveIntegerField()
cook_time = models.PositiveIntegerField()
servings = models.PositiveIntegerField()
class Instruction(models.Model):
number = models.PositiveIntegerField()
text = models.TextField()
recipe = models.ForeignKey(Recipe, related_name='instructions', on_delete = models.CASCADE)
class RecipeIngredient(models.Model):
ingredient = models.ForeignKey(Ingredient, on_delete = models.CASCADE)
recipe = models.ForeignKey(Recipe, on_delete = models.CASCADE)
quantity = models.PositiveIntegerField()
unit = models.CharField(max_length=30, null= False, blank=True)
Serializers:
class TagSerializer(serializers.ModelSerializer):
recipes = serializers.PrimaryKeyRelatedField(queryset = Recipe.objects.all(), many = True)
class Meta:
model = Tag
fields = ('id','name', 'recipes')
class InstructionSerializer(serializers.ModelSerializer):
class Meta:
model = Instruction
fields = ('id','number','text','recipe')
class IngredientSerializer(serializers.ModelSerializer):
class Meta:
model = Ingredient
fields = ('id','name')
class RecipeIngredientSerializer(serializers.ModelSerializer):
class Meta:
model = RecipeIngredient
fields = ('id','ingredient','recipe','quantity','unit')
class RecipeSerializer(serializers.ModelSerializer):
class Meta:
model = Recipe
fields = ('id','name','ingredients','tags','prep_time','cook_time', 'servings', 'instructions')
You can use nested serializers, You can change the RecipeSerializer as follows
class IngredientSerializer(serializers.ModelSerializer):
class Meta:
model = Ingredient
fields = '__all__'
class RecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientSerializer(many=True)
class Meta:
model = Recipe
fields = ('id','name','ingredients','tags','prep_time','cook_time', 'servings', 'instructions')
I have a serializer in my Django app that is meant for updating a nested object. Updating works, but I'm facing another problem: I can't delete objects that are not in validated_data['events] because I don't have the id to be compared with my instance id's.
For reference, these are my Models:
class Plan(models.Model):
planId = models.CharField(primary_key=True, max_length=100, unique=True)
name = models.CharField(max_length=200)
class PlanEvent(models.Model):
plan = models.ForeignKey(Plan, on_delete=models.CASCADE)
id = models.CharField(primary_key=True, max_length=100, unique=True, blank=False, null=False)
done = models.BooleanField()
title = models.CharField(max_length=100, blank=True)
This is my PlanEventUpdateSerializer:
class PlanEventUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = PlanEvent
fields = ('done', 'title')
Is there some way to include the id, so I could compare the id's like this in my update method:
class PlanUpdateSerializer(serializers.ModelSerializer):
events = PlanEventUpdateSerializer(many=True)
class Meta:
model = Plan
fields = ('name',)
....
def update(self, instance, validated_data):
events_validated_data = validated_data.pop('events')
events = (instance.events.all())
events = list(events)
event_ids = [item['id'] for item in events_validated_data]
for event in events:
if event.id not in event_ids:
event.delete()
I found a solution. I defined the id as a optional field in the serializer and then I was able to include it in the fields. Sending POST and PUT requests works now and I'm also able to delete objects when updating:
class PlanEventUpdateSerializer(serializers.ModelSerializer):
id = serializers.CharField(source='pk', required=False)
class Meta:
model = PlanEvent
fields = ('id', 'done', 'title')
I'm trying to extend Django's Poll app. Each poll will have 2 choices and either an image for each choice or a color for each choice, but not both. My models look like this:
class Question(models.Model):
question = models.CharField()
created_at = models.DateTimeField(auto_now_add=True)
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.CharField(max_length=120)
vote_count = models.IntegerField(default=0)
class ChoiceImage(models.Model):
choice = models.OneToOneField(Choice, on_delete=models.CASCADE)
img = models.ImageField()
class ChoiceColor(models.Model):
choice = models.OneToOneField(Choice, on_delete=models.CASCADE)
color = models.CharField(max_length=7)
I have a View that looks like this:
class CreateQuestionView(CreateAPIView):
serializer_class = CreateQuestionSerializer
With the following serializers:
class ChoiceImageSerializer(serializers.ModelSerializer):
class Meta:
model = ChoiceImage
fields = ('image',)
class ChoiceColorSerializer(serializers.ModelSerializer):
class Meta:
model = ChoiceColor
fields = ('color',)
class ChoiceSerializer(serializers.ModelSerializer):
choice_image = ChoiceImageSerializer(many=False)
choice_color = ChoiceColorSerializer(many=False)
class Meta:
model = Choice
fields = ('choice', 'choice_image', 'choice_color')
class CreateQuestionSerializer(serializers.ModelSerializer):
choices = ChoiceSerializer(many=True)
class Meta:
model = Question
fields = ('question', 'choices')
How do I create this constraint on my serializers? Also Am I designing this efficiently or is there a better way to do this?
My serializers are not showing related Models. I mean the models that have got many-to-one relationship. Please see the code below. Forgive my English.
Completely confusing me please help. I am new to django. I am trying to save my Draft js ContentState to the database. I have made the Post model a Foreignkey to my Block models. But When I try to retrieve the data using django-rest-framework serializers the blocks are not displaying.
Same applies with the Blocks I tried to serialize them on their own but the inlineStyleRanges and entityRanges data is not coming up.
#models.py
class Post(models.Model):
created_by= models.ForeignKey(User, on_delete=models.CASCADE)
cat= models.ForeignKey(Category, on_delete=models.CASCADE)
class Block(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
key = models.CharField(max_length=255)
text = models.TextField()
type = models.CharField(max_length=90)
depth = models.IntegerField(default=0)
class InlineStyleRange(models.Model):
block = models.ForeignKey(Block, on_delete=models.CASCADE)
offset = models.IntegerField()
length = models.IntegerField()
style = models.CharField(max_length=90)
class EntityRange(models.Model):
block = models.ForeignKey(Block, on_delete=models.CASCADE)
offset = models.IntegerField()
length = models.IntegerField()
key = models.IntegerField()
class Data(models.Model):
data = models.TextField()
class EntityRangeData(Data):
enityrange = models.ForeignKey(EntityRange, on_delete=models.CASCADE)
#Entity map here we go
class EntityEntry(models.Model):
key = models.IntegerField()
block= models.ForeignKey(Block, on_delete=models.CASCADE)
type = models.CharField(max_length=90)
mutability = models.CharField(max_length=90)
class EntityEntryData(Data):
entityentry = models.ForeignKey(EntityEntry, on_delete=models.CASCADE)
```#Serializers.py```
class EntityEntryDataSerializer(serializers.ModelSerializer):
class Meta:
model = models.EntityEntryData
fields = "__all__"
class EntityEntrySerializer(serializers.ModelSerializer):
data = EntityEntryDataSerializer()
class Meta:
model = models.EntityEntry
fields = "__all__"
class EntityRangeDataSerializer(serializers.ModelSerializer):
class Meta:
model = models.EntityRangeData
fields = "__all__"
class EntityRangeSerializer(serializers.ModelSerializer):
data = EntityRangeDataSerializer()
class Meta:
model = models.EntityRange
fields = "__all__"
class InlineStyleRangeSerializer(serializers.ModelSerializer):
class Meta:
model = models.InlineStyleRange
fields = "__all__"
class BlockSerializer(serializers.ModelSerializer):
inlineStyleRanges = InlineStyleRangeSerializer(many=True, required=False)
entityRanges = EntityRangeSerializer(many=True, required=False)
class Meta:
model = models.Block
fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
blocks = BlockSerializer(many=True, required=False, read_only=True)
class Meta:
model = models.Post
fields = "__all__"
The output be like
[
{
"id": 1,
"created_by": 1,
"cat": 2
}
]
The Block model does not have attributes called inlineStyleRanges or entityRanges, so you should either use related_name in the ForeignKey field or you could specify a source argument in the serializer.
inlineStyleRanges = InlineStyleRangeSerializer(many=True, required=False, source='inlinestyle_set')
entityRanges = EntityRangeSerializer(many=True, required=False, source='entityrange_set')
You might have to include both nested serializers in BlockSerializer.Meta.fields as well. Instead of "__all__", use a list:
fields = ['id', 'post', 'key', 'inlineStyleRanges', 'entityRanges']
i am developing a rest API using django rest framework and i am stuck at a serializer the idea is to serialize a self recursive many to many model using a through table my code is:
model.py:
class Patient(models.Model):
class Meta:
db_table = 'patients'
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
id_card = models.CharField(max_length=45)
dob = models.DateField()
gender = EnumChoiceField(enum_class=Gender)
patientscol = models.CharField(max_length=45)
fk_user = models.ForeignKey(Users, related_name='user_patient', on_delete=models.CASCADE)
relative = models.ManyToManyField("self", through='PatientHasRelative')
class PatientHasRelative(models.Model):
class Meta:
db_table = 'patients_has_relatives'
fk_patient = models.ForeignKey(Patient, related_name='patient_has', on_delete=models.CASCADE)
fk_relative_patient = models.ForeignKey(Patient, related_name='patient_relative', on_delete=models.CASCADE)
relationship = EnumChoiceField(enum_class=Relationship)
my serializer.py is:
class PatientSerializer(serializers.ModelSerializer):
class Meta:
model = Patient
fields = ('__all__')
id = serializers.UUIDField(read_only=True)
id_card = serializers.CharField(required=True, max_length=45)
dob = serializers.DateField(required=True)
gender = EnumChoiceField(enum_class=Gender)
fk_user = serializers.PrimaryKeyRelatedField(required=True, queryset=Users.objects.all())
relative = PatientSerializer(read_only=True, required=True)#problem is here i cant use PatientSerializer here
class PatientHasRelativeSerializer(serializers.ModelSerializer):
class Meta:
model = PatientHasRelative
fields = ('__all__')
fk_patient = serializers.PrimaryKeyRelatedField(required=True, queryset=Patient.objects.all())
fk_relative_patient = serializers.PrimaryKeyRelatedField(required=True, queryset=Patient.objects.all())
relationship = EnumChoiceField(enum_class=Relationship)
a little help would be appreciated
To accomplish this you need to define related_name in the source model on the source field ie add
class Patient(models.Model):
relatives = models.ManyToManyField(
"self", through='PatientHasRelative', related_name='patients')
with this related_name you can easily access -- add/delete/set relatives/patients on either side of the relationships in the serializers
You can either do this using intermediary model
relative = Patient(**key_value_fields)
patient = Patient(**key_value_field)
PatientHasRelative.objects.create(
relative=relative, patient=patient, through_defaults=(relationship ='value',))
or you can do this
relative.patients.add(patient, through_defaults=relationship ='value')
or this
patient.relatives.add(relative, through_defaults=relationship ='value')
example retrieving
patient.relatives.all()