Using django rest framework, I'm after this output:
slides = [
{
"id": "1",
"sentence": "The person is in the house.",
"sentence-type": "simple",
"clauses": {
"type": "independent clause",
"description": "An independent clause has a subject and a verb. An independent clause expresses a complete thought."
},
"wordlist": [
{
"pos": "article",
"pos_description": "An article modifies a noun like an adjective does and are considered necessary to provide proper syntax to a sentence.",
"colour": "21 128 61",
"word": "the"
},
Here are my models:
class Sentence(models.Model):
sentence = models.CharField(max_length=500)
class Word(models.Model):
word = models.CharField(max_length=255)
part_of_sentence = models.ForeignKey(PartOfSentence, related_name='pos_word', on_delete=models.CASCADE, blank=True, null=True)
words = models.ForeignKey(Sentence, related_name='sentence_word', on_delete=models.CASCADE, blank=True, null=True)
class PartOfSentence(models.Model):
part_of_sentence = models.CharField(max_length=20)
pos_colour = models.CharField(max_length=20)
pos_description = models.CharField(max_length=512)
class Clause(models.Model):
TYPES = (
('NONE', 'None'),
('INDEPENDENT', 'Independent'),
('DEPENDENT', 'Dependent'),
('COORDINATING_CONJUNCTION', 'Coordinating-Conjunction'),
('SUBORDINATING_CONJUNCTION', 'Subordinating-Conjunction')
)
clause = models.CharField(choices=TYPES, max_length=40, default='None')
And the serializers:
class ClauseSerializer(serializers.ModelSerializer):
class Meta:
model = Clause
fields = ('clause',)
class SentenceSerializer(serializers.ModelSerializer):
clause = ClauseSerializer()
words = serializers.SlugRelatedField(
source='sentence_word',
slug_field='word',
many=True,
read_only=True
)
# part_of_sentence = serializers.SlugRelatedField(
# many=True,
# slug_field='part_of_sentence',
# queryset=PartOfSentence.objects.all()
# )
class Meta:
model = Sentence
fields = ('sentence', 'sentence_type', 'clause', 'words')#, 'part_of_sentence'
Which results in:
[
{
"sentence": "the person is in the house",
"sentence_type": "SIMPLE",
"clause": {
"clause": "INDEPENDENT",
"description": "An independent clause has a subject and a verb. An independent clause expresses a complete thought"
},
"words": [
"the",
"person",
"is"
]
}
]
Which isn't perfect, i.e. the keys are missing for the words array along with the parts_of_words. I think I'm only the right track - my question is:
Can I do a forward lookup from the Word model using SlugRelated field?
Related
I have this JSON input which I'd like to express properly in the Django Model in order to be correctly serialized by Django Rest Framework.
the MainObject has name property and a list of conditions;
each Condition is composed exactly by 2 blocks: left and right;
each Block is composed by 1 field title and a list of user_params.
{
"name": "The Main Object Name",
"conditions": [
{
"left": {
"title": "Title1",
"user_params": [
{
"name": "name_param_X",
"value": "100"
}
]
},
"right": {
"title": "Title2",
"user_params": [
{
"name": "name_param_Y",
"value": "200"
}
]
}
}
]
}
And here my models:
class MainObject(models.Model):
main_object_id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=64)
class Condition(models.Model):
condition_id = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False, unique=True)
main_object = models.ForeignKey(MainObject, related_name="conditions",
on_delete=models.CASCADE)
left = models.OneToOneField(Block, on_delete=models.CASCADE, null=True, related_name='left')
right = models.OneToOneField(Block, on_delete=models.CASCADE, null=True, related_name='right')
class Block(models.Model):
title = models.CharField(max_length=16, primary_key=True)
class BlockParam(models.Model):
name = models.CharField(max_length=32)
value = models.IntegerField()
block_title = models.ForeignKey(Block, related_name="user_params",
on_delete=models.CASCADE)
My serializers:
class ConditionSerializer(serializers.ModelSerializer):
condition_id = serializers.UUIDField(required=False)
class Meta:
model = Condition
fields = ('condition_id', 'left', 'right')
class MainObjectSerializer(serializers.ModelSerializer):
conditions = ConditionSerializer(many=True)
def create(self, validated_data):
conditions_data = validated_data.pop("conditions")
main_object = MainObject.objects.create(**validated_data)
for condition_data in conditions_data:
Condition.objects.create(main_object=main_object, **condition_data)
return main_object
The issue: when I do a POST request to create a MainObject with the aforementioned JSON input, I receive 400 Bad response with this message:
{
"conditions": [
{
"left": [
"Invalid pk \"{'title': 'Title1', 'user_params': [{'name': 'name_param_x', 'value': '100'}]}\" - object does not exist."
],
"right": [
"Invalid pk \"{'title': 'Title2', 'user_params': [{'name': 'name_param_y', 'value': '200'}]}\" - object does not exist."
]
}
]
}
My questions:
Are the models correctly modelled according to the desired JSON input structure?
Do I need to modify the serializers in order to get it work?
I’m working with Django Rest to have multiple “text block” objects linked with the document object. I’ve accomplished this with a simple models.ForeignKey feature so far.
However, I’m rendering all of these text blocks in multiple columns in the front end.
The Textblock model will have a column field to determine which goes to which column. Since the order of these text blocks matter, I was afraid of having them all mixed together under single "all_columns" field
So far, I figured the easiest way is to let DRF return something like the following:
{
"name": "Comparing Two Characters",
"column_A": [
{
"title": "Text Block",
"body": "lorem ipsum blah blah"
"col": 1
}
],
"column_B": [
{
"title": "Text Block 2",
"body": "lorem ipsum blah blah"
"col": 2
},
{
"title": "Text Block 3",
"body": "lorem ipsum blah blah"
"col": 2
}
]
}
How would I be able to implement something like this? I’m not sure if using related fields is even ideal for such cases. I would appreciate any help!
Here’s my current models.py code for reference:
class Document(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
# other fields
def __str__(self):
return self.name
class TextBlock(models.Model):
id = models.AutoField(primary_key=True)
document = models.ForeignKey(Document, related_name='blocks', on_delete=models.CASCADE)
col = models.IntegerField()
title = models.CharField(max_length=100)
body = models.CharField(max_length=100)
Edit:
What I'm getting returned with the updated code by sayeed910
"name": "outlineblock-test",
"desc": "",
"blocks": [
{
"url": "http://0.0.0.0:8000/api/v1/layoutblocks/7/",
"col": 3,
"title": "col3",
"placeholder": "hehehe",
"template": "http://0.0.0.0:8000/api/v1/templates/3/"
},
{
"url": "http://0.0.0.0:8000/api/v1/layoutblocks/6/",
"col": 2,
"title": "col2",
"placeholder": "hehe",
"template": "http://0.0.0.0:8000/api/v1/templates/3/"
},
{
"url": "http://0.0.0.0:8000/api/v1/layoutblocks/5/",
"col": 1,
"title": "col1",
"placeholder": "haha",
"template": "http://0.0.0.0:8000/api/v1/templates/3/"
}
],
serializers.py
class TextBlockSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TextBlock
fields = '__all__'
class DocumentSerializer(serializers.HyperlinkedModelSerializer):
blocks = LayoutBlockSerializer(many=True, read_only=True)
class Meta:
model = Document
fields = '__all__'
Try this:
from collections import defaultdict
class Document(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
# other fields
def __str__(self):
return self.name
def blocks_by_column(self):
block_group = defaultdict(list)
for block in self.blocks.all():
block_group[block.col].append(block)
return block_group
class TextBlock(models.Model):
id = models.AutoField(primary_key=True)
document = models.ForeignKey(Document, related_name='blocks', on_delete=models.CASCADE)
col = models.IntegerField()
title = models.CharField(max_length=100)
body = models.CharField(max_length=100)
If you have a ordering mechanism i.e. an order column, you can change self.blocks.all() to self.blocks.order_by(<column>).all(). You can later change the keys of blocks_group as you see fit. 1 -> column_A.
As Per OP's Edit:
You should perform the grouping operation in the serializer instead of the model
from collections import defaultdict
class DocumentSerializer(serializers.HyperlinkedModelSerializer):
blocks = LayoutBlockSerializer(many=True, read_only=True)
def to_representation(self, instance):
ret = super().to_representation(instance)
block_group = defaultdict(list)
for block in ret["blocks"]:
block_group[block["col"]].append(block)
ret["blocks"] = dict(block_group)
return ret
class Meta:
model = Document
fields = '__all__'
I have a model Jurisdiction that has M2M relation with Franchise. And I use ModelViewSet on Jurisdiction.
models.py
class Franchise(models.Model):
name = models.CharField('Name', max_length=255, db_index=True)
class Jurisdiction(models.Model):
franchise = models.ManyToManyField(
Franchise,
verbose_name='Franchise',
related_name='jurisdictions',
blank=True
)
name = models.CharField(
"Jurisdiction name",
max_length=255,
db_index=True,
unique=True,
)
phone_number = models.CharField(
"Phone number",
max_length=12,
validators=[phone_number_regex],
unique=True,
)
My views.py
class JurisdictionViewSet(viewsets.ModelViewSet):
queryset = Jurisdiction.objects.all().prefetch_related('franchise')
serializer_class = JurisdictionSerializer
serializers.py
class JurisdictionSerializer(serializers.ModelSerializer):
franchise = FranchiseSerializer(many=True)
class Meta:
model = Jurisdiction
fields = (
'id', 'name', 'phone_number', 'franchise',
)
And serializer shows me:
{
"id": 1,
"name": "Test juris 1",
"phone_number": "200-000-1233",
"franchise": [
{
"id": 1,
"name": "Test franchise 1",
},
{
"id": 2,
"name": "Test franchise 2",
}
}
How I can get a list view of every m2m relation as a different object in serializer? I tried to change the to_representation method of serializer but nothing works
expected output:
[
{
"id": 1,
"name": "Test juris 1",
"phone_number": "200-000-1233",
"franchise_name": "Test franchise 1"
},
{
"id": 1,
"name": "Test juris 1",
"phone_number": "200-000-1233",
"franchise_name": "Test franchise 2"
}
]
You can customize the ListSerializer of your ModelSerializer. Specifically the to_representation method. For example:
from rest_framework import serializers
class CustomJurisdictionListSerializer(serializers.ListSerializer):
def to_representation(self, data):
iterable = data.all() if isinstance(data, models.Manager) else data
response = []
for item in iterable:
item_representation = self.child.to_representation(item)
for franchise in item.franchise.all():
representation = item_representation.copy()
representation['franchise_name'] = franchise.name
response.append(representation)
return response
Then remove franchise from JurisdictionSerializer and set the custom list serializer:
class JurisdictionSerializer(serializers.ModelSerializer):
class Meta:
model = Jurisdiction
fields = (
'id', 'name', 'phone_number',
)
list_serializer_class = CustomJurisdictionListSerializer
I have a Django model like this:
class Sections(models.Model):
section_id = models.CharField(max_length=127, null=True, blank=True)
title = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
class Risk(models.Model):
title = models.CharField(max_length=256, null=False, blank=False)
section = models.ForeignKey(Sections, related_name='risks')
class Actions(models.Model):
title = models.CharField(max_length=256, null=False, blank=False)
section = models.ForeignKey(Sections, related_name='actions')
And serializers like that :
class RiskSerializer(serializers.ModelSerializer):
class Meta:
model = Risk
fields = ('id', 'title',)
class ActionsSerializer(serializers.ModelSerializer):
class Meta:
model = Actions
fields = ('id', 'title',)
class RiskActionPerSectionsSerializer(serializers.ModelSerializer):
risks = RiskSerializer(many=True, read_only=True)
actions = ActionsSerializer(many=True, read_only=True)
class Meta:
model = Sections
fields = ('section_id', 'risks', 'actions')
depth = 1
def to_representation(self, instance):
response_dict = dict()
response_dict[instance.section_id] = {
'actions': HealthCoachingActionsSerializer(instance.actions.all(), many=True).data,
'risks': HealthCoachingRiskSerializer(instance.risks.all(), many=True).data
}
return response_dict
When accessing the RiskActionPerSectionSerializer over a view, I get the following output:
[
{
"section 1": {
"risks": [],
"actions": []
}
},
{
"section 2": {
"risks": [],
"actions": []
}
},
.....
]
It s fine but I would prefer to have that :
[
"section 1": {
"risks": [],
"actions": []
},
"section 2": {
"risks": [],
"actions": []
}
.....
]
Also why is it returning an array by default [] and not an object for example like:
{
"count": 0,
"next": null,
"previous": null,
"results": []
}
You need to override the to_representation() method of your serializer class. It is mention in brief in this official documentation link:
http://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior
Other option is overriding the get and post methods of your class APIView or it's inherited class
You can't store key/value pairs in list, ['key': {}] won't work, you should access to item by index instead, like in your representation.
Array returned because of many=True which means you put many objects in serializer and it wait sequence from you.
I can't see the ORM query that resulted in this output, but Your query is returning multiple items that match your criteria. Default behaviour of any frameworks, let alone DRF, is to put them inside an array and return them as an "Array of objects" as in your result.
I have the following models in my Django Application. The BasicInfo and AddressInfo tables are linked to the Customer table using the id column of the Customer Table, which is the customer_id column in these tables.
For each customer, there can be multiple entries in the AddressInfo or the BasicInfo table.
class Customer(models.Model):
id = models.IntegerField(primary_key=True)
joining_dtm = models.DateTimeField()
isactive = models.IntegerField()
class Meta:
db_table = u'Customer'
class Addressinfo(models.Model):
id = models.IntegerField(primary_key=True)
apt_num = models.CharField(max_length=135, blank=True)
street = models.CharField(max_length=135, blank=True)
street_2 = models.CharField(max_length=135, blank=True)
city = models.CharField(max_length=135, blank=True)
country = models.CharField(max_length=135, blank=True)
postalcode = models.CharField(max_length=135, blank=True)
customer_id = models.ForeignKey(Customer)
class Meta:
db_table = u'AddressInfo'
class Basicinfo(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=135, blank=True)
about = models.CharField(max_length=135, blank=True)
website = models.CharField(max_length=135, blank=True)
customer_id = models.ForeignKey(Customer)
class Meta:
db_table = u'BasicInfo'
How do I create a single Tastypie resource which will enable me to provide all customer tables as an API.
The json object that the resource returns can be something similar to the following for the customer with id 12,
{
"id": 12,
"joining_dtm": "2012-10-25T07:06:54.041528",
"resource_uri": "/public-api/api/v0.1a/customer/1/",
"AddressInfo": [
{
"id":22,
"apt_num": "54",
"street": "Avondale Place",
"street_2": "",
"city": "Chicago",
"country": "India",
"postalcode": "600059",
"customer_id": 12
},
{
"id":96,
"apt_num": "11",
"street": "Infinite Loop",
"street_2": "",
"city": "Cupertino",
"country": "USA",
"postalcode": "123456",
"customer_id": 12
}
],
"BasicInfo": [
{
"id": 33,
"name": "My Full Name",
"about": "Blah Blah Blah",
"website": "http://google.com",
"customer_id": 12
},
{
"id": 147,
"name": "My New Name",
"about": "More Blah Blah",
"website": "http://gmail.com",
"customer_id": 12
}
]
}
If you are simply asking for an API that lists all of your customers:
/public-api/api/v0.1a/customer/?format=json
Should give you that. Otherwise, below is what I assume you were asking - how to set up the resources to show basic info / address info when viewing a customer's endpoint
In your api.py:
class CustomerResource(ModelResource):
# The 2nd parameter is tricky:
# Try suffixing with nothing, 's', or '_set'
# e.g. 'addressinfo', 'addressinfos', 'addressinfo_set'
addressinfos = fields.ToManyField('your_app_name.api.AddressInfoResource', 'addressinfo_set', Full=True)
basicinfos = fields.ToManyField('your_app_name.api.BasicInfoResource', 'basicinfo_set', Full=True)
class Meta:
queryset = Customer.objects.all()
resource_name = 'customer'
class AddressInfoResource(ModelResource):
class Meta:
queryset = Addressinfo.objects.all()
resource_name = 'addressinfo'
class BasicInfoResource(ModelResource):
class Meta:
queryset = Basicinfo.objects.all()
resource_name = 'basicinfo'
And of course add authentication / authorization at each of these resources as needed.