Marshmallow nested relationship beyond one level deep - flask

I have the following schemas in marshmallow that rely on models built with sqlalchemy. I am able to show an API response with multiple subscription prices that come from a many-to-many relationship off of the Journal model that is called 'subscription_prices'.
from marshmallow import fields
class SubscriptionPriceSchema(ma.Schema):
price = fields.Decimal(as_string=True)
class Meta:
model = SubscriptionPrice
fields = (
"price",
"year",
)
class JournalSchema(ma.Schema):
subscription_prices = fields.Nested(SubscriptionPriceSchema, many=True)
class Meta:
model = Journal
fields = (
"title",
"subscription_prices"
)
However, what I want to do is add a field called 'provenance' that is at the same level of the subscription prices. So I tried to do this and it is not working:
from marshmallow import fields
class SubscriptionPriceSchema(ma.Schema):
price = fields.Decimal(as_string=True)
class Meta:
model = SubscriptionPrice
fields = (
"price",
"year",
)
class SubscriptionSchema(ma.Schema):
provenance = fields.String(attribute="publisher.provenance")
subscription_prices = fields.Nested(SubscriptionPriceSchema, many=True)
class Meta:
fields = (
"provenance",
"subscription_prices",
)
class JournalSchema(ma.Schema):
subscription_prices = fields.Nested(SubscriptionSchema)
class Meta:
model = Journal
fields = (
"title",
"subscription_prices"
)
It seems like the relationship 'subscription_prices' is not passed in beyond one level, so it does not reach the new SubscriptionSchema. Any ideas on how I can make this work?

Related

How can I add column to the admin from other table, that isn't directly connected?

I have four tables connected together:
Meeting (two foreign keys of Person)
Person (many to many field to 'ConditionDictionary'
Condition (foreign key of Person and ConditionDictionary)
ConditionDictionary
my Meeting table has foreign key of Person and Person is connected many-to-many to ConditionDictionary, which goes through table Condition (because I had to add extra flags there).
I want to add one column from ConditionDictionary to meeting view, but I have no idea how can I do it.
When running in python shell, I achieve what I need
Meeting.objects.filter(id=1).values('person_in_need__person_c__condition__issue')
But, as mentioned above, I have no idea how to add the option to choose what it returns on the meeting class.
Here are the models (the important bits):
class ConditionDictionary(models.Model):
issue = models.CharField(max_length=50)
description = models.TextField()
class Condition(models.Model):
person = models.ForeignKey(Person, related_name = 'person_c', on_delete=models.CASCADE)
condition = models.ForeignKey(ConditionDictionary, related_name = 'condition_c', on_delete=models.CASCADE)
class Person(models.Model):
issue = models.ManyToManyField(ConditionDictionary, through='Condition')
class Meeting(models.Model):
person1 = models.ForeignKey(Person, related_name='in_need', on_delete=models.CASCADE)
person2 = models.ForeignKey(Person, related_name='carer', on_delete=models.CASCADE)
My idea is that with every meeting created, you could choose what issue is treated this time.
You can show related table data using django's __ (double underscore).
For example, I've got a Profile model which links to User. User also links to a Passport model.
You can see here in the search_fields that it's going to do a search on the ID of the User and also through the User table to the Passport table to search that by ID;
#admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
""" Model admin for Profile. """
# List attributes.
list_display = (
'id',
'user',
)
list_filter = (
'series_entrant',
)
search_fields = (
'user__id',
'user__email',
'user__first_name',
'user__last_name',
'user__passport__id'
)
To display related data in the fields of an admin class you can make a callable and add it to the readonly fields. For example;
#admin.register(AddOn)
class AddOnAdmin(admin.ModelAdmin):
"""
AddOn model admin
"""
list_display = (
'id',
'get_related',
'enabled',
'created',
)
readonly_fields = (
'created',
'modified',
'get_related',
)
fields = (
'enabled',
'get_related',
'name',
'description',
'created',
'modified',
)
def get_related(self, obj):
"""
Get some related data
"""
return obj.realted_model.field
get_related.short_description = _('Related')

Django: what is difference between to set CHOICES in models and in forms?

I think that only defining ChoiceField in forms is best solution if I'm 100% sure to use form, right?.
I mean, I don't have to do like this:
models.py
class Payment(TimeStampedModel):
PAY_METHOD_CHOICES = (
('card', '카드'),
('cash', '무통장입금'),
)
pay_method = models.CharField(
max_length=10,
choices=PAY_METHOD_CHOICES
)
forms.py
class PaymentForm(forms.ModelForm):
PAY_METHOD_CHOICES = (
('card', '카드'),
('cash', '무통장입금'),
)
pay_method = forms.ChoiceField(
choices=PAY_METHOD_CHOICES
)
class Meta:
model = Payment
This is redundant code, isn't it?
In this sense, I can not understand why use choices in models field...
When does it being used? which case?
Thanks
As in document states https://docs.djangoproject.com/en/1.10/ref/models/fields/#choices
If this is given, the default form widget will be a select box with these choices instead of the standard text field.
So basically choices param is used to construct form automatically when you do
class PaymentForm(forms.ModelForm):
class Meta:
model = Payment
fields = '__all__'
It will set form field based on your model.
Answering comment
If you need to customize form fields you could use https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/#overriding-the-default-fields
class PaymentForm(forms.ModelForm):
class Meta:
model = Payment
fields = '__all__'
labels = {
'pay_method': _('New Label'),
}

Add Serializer on Reverse Relationship - Django Rest Framework

I have a Cart model and a CartItem model. The CartItem model has a ForeignKey to the Cart model.
Using Django Rest Framework I have a view where the API user can display the Cart, and obviously then I want to include the CartItem in the respone.
I set up my Serializer like this:
class CartSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
cartitem_set = CartItemSerializer(read_only=True)
class Meta:
model = Cart
depth = 1
fields = (
'id',
'user',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
My problem is the second line, cartitem_set = CartItemSerializer(read_only=True).
I get AttributeErrors saying 'RelatedManager' object has no attribute 'product'. ('product' is a field in the CartItem model. If I exclude product from the CartItemSerializer I just get a new AttributeError with the next field and so on. No matter if I only leave 1 or all fields in the Serializer, I will get a error.
My guess is that for some reason Django REST Framework does not support adding Serializers to reverse relationships like this. Am I wrong? How should I do this?
PS
The reason why I want to use the CartItemSerializer() is because I want to have control of what is displayed in the response.
Ahmed Hosny was correct in his answer. It required the many parameter to be set to True to work.
So final version of the CartSerializer looked like this:
class CartSerializer(serializers.ModelSerializer):
cartitem_set = CartItemSerializer(read_only=True, many=True) # many=True is required
class Meta:
model = Cart
depth = 1
fields = (
'id',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
It's important to define a related name in your models, and to use that related name in the serializer relationship:
class Cart(models.Model):
name = models.CharField(max_length=500)
class CartItem(models.Model):
cart = models.ForeignKey(Cart, related_name='cart_items')
items = models.IntegerField()
Then in your serializer definition you use those exact names:
class CartSerializer(serializers.ModelSerializer):
cart_items = CartItemSerializer(read_only=True)
class Meta:
model = Cart
fields = ('name', 'cart_items',)
It would be wise to share your whole code, that is model and serializers classes. However, perhaps this can help debug your error,
My serializer classes
class CartItemSerializer(serializers.ModelSerializer):
class Meta:
model = CartItem
fields = ('id')
class CartSerializer(serializers.ModelSerializer):
#take note of the spelling of the defined var
_cartItems = CartItemSerializer()
class Meta:
model = Cart
fields = ('id','_cartItems')
Now for the Models
class CartItem(models.Model):
_cartItems = models.ForeignKey(Subject, on_delete=models.PROTECT)
#Protect Forbids the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT.
class Meta:
ordering = ('id',)
class Cart(models.Model):
class Meta:
ordering = ('id',)
For a detailed overview of relationships in django-rest-framework, please refer their official documentation

Storing multiple values in django model though serializer

I am fairly new to django rest framework. I have these tables in my database:
1) MainCategroies - which stores a list of all education fields.
2) College - which stores list of all college of my state.
3) CollegeCategoryLink - which stores the link between colleges and the categories to which they belong( here same college can fall under multiple categories). created a model with two foreign-key column
4) Users - the users of my app.
5) UserCategoryLink - link between the users and their selected categories. created a model with two foreign-key column
6) UserCollegeLink - link between the users and their selected colleges. created a model with two foreign-key column
Now the users will select their preferable categories from the list
and that will be stored in my database and then i will return the
related colleges back. All the data will come in json format from my
ionic app.
i have written serializers for each model and created viewsets for CRUD operations. Now i am confused, how to store the data through viewset methods? I am currently doing this:
class UserCategoryLinkViewset(viewsets.ViewSet):
serializer_class = UserCategoryLinkSerializer
def create(self, request):
selectedCats = []
collegeList = []
data = JSONParser().parse(request)
for field in data:
selectedCats.append(field['cat'])
ucl = UserCategoryLink()
ucl.user = collegeAppUser.objects.get(id=field['user'])
ucl.cat = MainCategories.objects.get(id=field['cat'])
if not UserCategoryLink.objects.filter(user=field['user'], cat=field['cat']).exists():
ucl.save()
for cats in selectedCats:
queryset = CollegeCategoryLink.objects.filter(category_id=cats)
serializer = CollegeCategoryLinkSerializer(queryset, many=True)
for clg in serializer.data:
queryset_college = College.objects.filter(id=clg['college_id'])
serializer_college = CollegeSerializer(queryset_college, many=True)
collegeList.append(serializer_college.data)
return JSONResponse(collegeList)
And here are my serializers:
from rest_framework import serializers
from manageApp.models import collegeAppUser,MainCategories,UserCategoryLink, CollegeCategoryLink, College, UserCollegeLink
class collegeAppUserSerializer(serializers.ModelSerializer):
class Meta:
model = collegeAppUser
fields = ('id', 'username', 'password')
class MainCategorySerializer(serializers.ModelSerializer):
class Meta:
model = MainCategories
fields = ('id', 'category_name')
class UserCategoryLinkSerializer(serializers.ModelSerializer):
class Meta:
model = UserCategoryLink
fields = ('id', 'user', 'cat')
class CollegeCategoryLinkSerializer(serializers.ModelSerializer):
class Meta:
model = CollegeCategoryLink
fields = ('id', 'college_id', 'category_id')
class CollegeSerializer(serializers.ModelSerializer):
class Meta:
model = College
fields = ('id', 'college_name', 'college_url')
class UserCollegeLinkSerializer(serializers.ModelSerializer):
class Meta:
model = UserCollegeLink
fields = ('id', 'user', 'college')
But this is not the correct way to accomplish what i need to do as i am directly setting the data in my model and saving it, no use of serializer in here. I want to store the data through serializer rather then directly using my model.
Try to relate models.
You can see the rest framework documentation: Relations!
For your case, you would use Nested relationships! =]

Using rest django rest framework for creating new object

I have the following models
class Peri(models.Model):
date = models.DateField()
customer = models.ForeignKey(Customer)
class PeriTask(models.Model):
#fields
peri = models.ForeignKey(Peri)
My serializers are the following
class PeriSerializer(serializers.HyperlinkedModelSerializer):
customer = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Peri
fields = ('id', 'date', 'url', 'peritasks', 'customer')
class PeriTaskSerialiazer(serializers.HyperlinkedModelSerializer):
tooth = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = PeriTask
fields = ('id', 'task_type', 'implant', 'furcation', 'bleeding1', 'bleeding2', 'bleeding3', 'plaque1', 'plaque2',
'gingival_margin1', 'gingival_margin2', 'gingival_margin3', 'probing_depth1', 'probing_depth2',
'probing_depth3', 'tooth', 'url', )
and my viewsets are
class PeriodontogrammaViewSet(ModelViewSet):
serializer_class = PeriSerializer
queryset = Peri.objects.all()
class PeriTaskViewSet(ModelViewSet):
serializer_class = PeriTaskSerialiazer
queryset = PeriTask.objects.all()
But when I try to create a new peri using the api it gives me the following integrity error
NOT NULL constraint failed: peri_peri.customer_id
My json data that beeing posted are
{"date": "2014-12-17",
"customer": 27
}
I haven't created a serializer for customer since I am not interested in having api for my other models.
In your serializer, you've set the customer key to read_only:
customer = serializers.PrimaryKeyRelatedField(read_only=True)
Try setting it to False or just simply removing this whole line (which seems superfluous to me)