I want to serializing ManyToMany fields. So, in the response for tje Application model should also the band and the band description listed. But I've got the following error message:
Got AttributeError when attempting to get a value for field bands on
serializer OsdSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the Application
instance. Original exception text was: 'Application' object has no
attribute 'bands'.
Anyone who knows why it doesnt work?
models.py
class Band(models.Model):
"""Database models for satellit's bands information"""
band = models.CharField(max_length=3)
description = models.CharField(max_length=255)
in_satellite = models.ForeignKey('Satellite', on_delete=models.PROTECT, blank=True, null=True)
wavelength = models.DecimalField(max_digits=4, decimal_places=0, default='0', help_text='Central wavelength (nm)')
resolution = models.DecimalField(max_digits=4, decimal_places=0, default='0', help_text='Spatial resolution (m)')
def __str__(self):
return '%s | %s' % (self.in_satellite, self.band)
class Satellite(models.Model):
"""Database models for satellite information"""
name = models.CharField(max_length=255)
accr = models.CharField(max_length=3)
operator = models.CharField(max_length=255, default='European Space Agency')
def __str__(self):
return self.name
class Indice(models.Model):
"""Database models for index information"""
name = models.CharField(max_length=255)
accr = models.CharField(max_length=10)
description = models.TextField(max_length=255, blank=True, null=True)
satellite_to_use = models.ForeignKey('Satellite', on_delete=models.PROTECT, blank=True, null=True)
needed_bands = models.ManyToManyField(Band)
def __str__(self):
return self.name
class Application(models.Model):
"""Database models for application information"""
name = models.CharField(max_length=255)
description = models.TextField(max_length=255, blank=True, null=True)
indice_to_use = models.ForeignKey('Indice', on_delete=models.PROTECT, blank=True, null=True)
def __str__(self):
return self.name
serializers.py
class BandSerializer(serializers.ModelSerializer):
class Meta:
model = Band
fields = ['band', 'description', ]
class OsdSerializer(serializers.ModelSerializer):
bands = BandSerializer()
class Meta:
model = Application
fields = ['name', 'description', 'bands',]
views.py
class OsdView(APIView):
def get(self, request):
applications = Application.objects.all()
serializer = OsdSerializer(applications, many=True)
return Response({"Your open space data:": serializer.data})
IIUC, you need to change the OsdSerializer to specify the relation you want to include in the serialized data:
bands = BandSerializer(source='indice_to_use.needed_bands', many=True)
Your code is correct but here i don't see any relation between two of your models Band and Application.
I think you might need to find some relation between Band model and Application model only then and then you will get the band object in output associated with corresponding application object.
Related
In DRF I am facing an issue, whenever I do a POST request on the endpoint, on the field "name" which is a text field I get an exception "Field 'id' expected a number but got 'TITLE'", but when I change the value of "name" to an integer the request is successful I don't understand it becauses name is TextField in model and why its mixing Id and Name field with each other. I have deleted the migration files from the Project and DB and re-run the Migrations, but still facing this issue.
Following is my code:
models.py
class Project(models.Model):
admin = models.ForeignKey(User, on_delete=models.CASCADE, related_name='project_crated_by')
name = models.TextField(max_length=225, blank=False, null=False)
project_members = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='members', null=True, blank=True)
created_on = models.DateField(default=timezone.now)
tags = ArrayField(models.CharField(max_length=225, default=''), blank=True)
def __str__(self):
return self.name
objects = models.Manager()
views.py
class ProjectView(viewsets.ViewSet):
def create(self, request):
project_name_exist = Project.verify_project_name(request.data['admin'], request.data['name'])
if project_name_exist:
return Response({'message': 'You already have a project with this name',
'status': status.HTTP_200_OK})
serialized_project = ProjectSerializer(data=request.data)
if serialized_project.is_valid():
serialized_project.save()
return Response({'message': 'Project Created Successfully', 'status': status.HTTP_201_CREATED})
else:
return Response({'error': serialized_project.errors, 'status': status.HTTP_400_BAD_REQUEST})
serializer.py
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = '__all__'
A more generic and non-DIY solution is to use UniqueTogetherValidator on your serializer and let Django sort it out.
from rest_framework.validators import UniqueTogetherValidator
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = '__all__'
validators = [
UniqueTogetherValidator(
queryset=Project.objects.all(),
fields=['admin', 'name'],
message='You already have a project with this name'
)
]
And/or add it to the model for enforcing it on the database.
class Project(models.Model):
admin = models.ForeignKey(User, on_delete=models.CASCADE, related_name='project_crated_by')
name = models.TextField(max_length=225, blank=False, null=False)
project_members = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='members', null=True, blank=True)
created_on = models.DateField(default=timezone.now)
tags = ArrayField(models.CharField(max_length=225, default=''), blank=True)
def __str__(self):
return self.name
objects = models.Manager()
class Meta:
unique_together = ("admin", "name")
I followed Dennis Ivy proshop Tutorial He used the same approach as the code is
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = '__all__'
class ProductSerializer(serializers.ModelSerializer):
reviews = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Product
fields = '__all__'
def get_reviews(self, obj):
reviews = obj.review_set.all()
serializer = ReviewSerializer(reviews, many=True)
return serializer.data
Now I need a Blog for the eCommerce Project and I created another app named blog and Created the models as
class BlogPost(models.Model):
_id = models.AutoField(primary_key=True, editable=False)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
title = models.CharField(max_length=200, null=True, blank=True, help_text="Like How To Treat Hypertension etc")
image = models.ImageField(null=True, blank=True,
default='/placeholder.png')
rating = models.DecimalField(
max_digits=7, decimal_places=2, null=True, blank=True)
numReviews = models.IntegerField(null=True, blank=True, default=0)
createdAt = models.DateTimeField(auto_now_add=True)
youtubeVideoLink = models.CharField(max_length=1000, null=True , blank=True)
def __str__(self):
return str(self.createdAt)
class BlogPostReview(models.Model):
blogpost = models.ForeignKey(BlogPost, on_delete=models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200, null=True, blank=True)
rating = models.IntegerField(null=True, blank=True, default=0)
comment = models.TextField(null=True, blank=True)
createdAt = models.DateTimeField(auto_now_add=True)
_id = models.AutoField(primary_key=True, editable=False)
def __str__(self):
return str(self.rating)
But when I serialize them via same approach as mentioned above....
class BlogPostReviewSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPostReview
fields = '__all__'
class BlogPostSerializer(serializers.ModelSerializer):
blog_post_reviews = serializers.SerializerMethodField(read_only=True)
class Meta:
model = BlogPost
fields = '__all__'
def get_blog_post_reviews(self, obj):
blog_post_reviews = obj.review_set.all()
serializer = BlogPostReviewSerializer(blog_post_reviews, many=True)
return serializer.data
This error comes
in get_blog_post_reviews
blog_post_reviews = obj.review_set.all()
AttributeError: 'BlogPost' object has no attribute 'review_set'
How to solve this problem or what I'm doing wrong and what need to be fixed. What would be another apporach obv there would be.... And I don't know why Dennis Ivy used review_set in his code. If someone know why we use _set and what are the circumstances please let me know.
The simplest solution is to update your get_blog_post_reviews method:
def get_blog_post_reviews(self, obj):
blog_post_reviews = obj.blogpostreview_set.all() # <- this line has changed
serializer = BlogPostReviewSerializer(blog_post_reviews, many=True)
return serializer.data
The original worked because there was a model named Review, so the automatically created reverse name was review_set. Your model is named BlogPostReview, so the reverse is blogpostreview_set.
More information about reverse relationships in the docs.
I have two models:
class Album(models.Model):
code = models.CharField(max_length=10, primary_key=True, default=_create_access_code, verbose_name=_("Id"))
name = models.CharField(max_length=200, verbose_name=_("Name"))
description = models.TextField(null=True, blank=True, verbose_name=_("Description"))
company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name='albums', verbose_name=_("Company"))
access_code = models.CharField(max_length=10, default=_create_access_code, verbose_name=_("Internal Use"))
class Meta:
verbose_name = _("Album")
verbose_name_plural = _("Albums")
def __str__(self):
return "[{}] {} ({})".format(self.pk, self.name, self.company.id)
class Photo(models.Model):
name = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Name"))
album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name='photos', verbose_name=_("Album"))
photo = models.ImageField(verbose_name=_("Photo"))
class Meta:
verbose_name = _("Photo")
verbose_name_plural =_("Photos")
def __str__(self):
return "[{}] {}".format(self.pk, self.name)
I am trying to make a post to the ModelViewSet for model Albums, but I get an error indicating that field photos is required. Even the OPTIONS method indicates it es required.
How can I instruct DRF for not considering look up fields as required? Is it some serializer setting?
You can add required=False to fields in the serializer.
photos = PhotoSerializer(many=True, required=False)
Something like this. Can you post you serializers?
I have the following models
class Company(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe a company'
)
url = models.URLField('company URL', blank=True, null=True)
email = models.EmailField(blank=True, null=True)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'company'
verbose_name_plural = 'companies'
ordering = ['name', '-created_on']
def __repr__(self):
return '<Company {0.name}>'.format(self)
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe the project'
)
company = models.ForeignKey(
Company,
on_delete=models.PROTECT,
)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'project'
verbose_name_plural = 'projects'
ordering = ['-created_on', 'company']
permissions = (
("can_view_project",
"Can view all project related work"),
)
def __repr__(self):
return '<Project {0.name}>'.format(self)
def __str__(self):
return self.name
class Worker(models.Model):
description = models.TextField(
max_length=500,
blank=True,
help_text='Optional. Describe what the worker does or who they are'
)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
company = models.ForeignKey(Company)
class Meta:
order_with_respect_to = 'user'
def __repr__(self):
return '<Worker {0.id}'.format(self)
def __str__(self):
return self.user.get_fullname()
The problem
I would like to add a ManyToMany relationship between Project and Worker so that I can view
a list of workers under a certain project. However, I want to make sure that a worker can only
be added to a project if they are both part of the same company.
I was planning on using a junction table with a ForeignKey to both of their company attributes,
but according to the django docs, a foreignkey can only be used once per model
(https://docs.djangoproject.com/en/1.10/topics/db/models/#extra-fields-on-many-to-many-relationships)
How do I make sure that the many to many relationship between the two tables is limited to the same company?
Is there perhaps another way to ensure that workers cannot work on projects outside of their own company?
Assuming you define the many to many relationship this way in the Project model:
workers = ManyToManyField(Worker)
Assuming you have a model form named ProjectForm to create or modify projects. You can define a clean function in this form:
def clean(self):
cleaned_data = super(ProjectForm, self).clean()
for w in cleaned_data['workers']:
if w.company.id != cleaned_data['company'].id:
self.add_error('workers', your_error_message)
break
return cleaned_data
Hope this help.
Hi I need to add a field in a serializer of a 2 level reference item.
I have the following model:
model.py:
class Company(models.Model):
companyName = models.CharField(max_length=50, blank=True)
class Poll(models.Model):
questionString = models.CharField(max_length=500, blank=True)
companyId = models.ForeignKey(Company, null=True, db_column='companyId', blank=True)
class PossibleAnswer(models.Model):
answerString = models.CharField(max_length=100, blank=True)
pollId = models.ForeignKey(Poll, null=True, db_column='pollId', blank=True,related_name='answers')
token = models.CharField(max_length=10, blank=True)
serializers.py:
class PossibleAnswerSerializer(serializers.ModelSerializer):
#companyId = serializers.RelatedField()
class Meta:
model = PossibleAnswer
fields = ('answerString', 'token', 'pollId', 'companyId')
I want to make a Serializer for the PossibleAnswer object that has a field named company. How to make a this reference? Something similar to: pollId__companyId in a django query set filter.
Another solution...
class PossibleAnswerSerializer(serializers.ModelSerializer):
companyId = serializers.SerializerMethodField()
def get_companyId(self, obj):
return obj.pollId.companyId
class Meta:
model = PossibleAnswer
fields = ('answerString', 'token', 'pollId', 'companyId',)
I the field is read-only you can easily achieve this with a serializers.Field, which accept dotted paths to the source.
Your Serializer would be:
class PossibleAnswerSerializer(serializers.ModelSerializer):
companyId = serializers.Field(source='pollId.companyId')
class Meta:
model = PossibleAnswer
fields = ('answerString', 'token', 'pollId', 'companyId')
I too agree with Erik, that naming model attributes with Id is a bad idea even though the DB representation is only the ID.