this is my code
class Brand(models.Model):
name = models.CharField(max_length=255)
logo = models.ImageField(upload_to=generate_filename_brand_image) #over write
add_to_explore = models.BooleanField(default=False)
def __str__(self):
return self.name
class Item(models.Model):
brand = models.ForeignKey('core.Brand', on_delete=models.CASCADE, null=True ,blank=True)
class ItemSerializers(serializers.ModelSerializer):
brand_logo = serializers.CharField(source='brand.logo.url')
class Meta:
model = Item
fields = ['id','brand_logo']
if image None I got this erorr
The 'image' attribute has no file associated with it.
How to handel this erorr
You can use SerializerMethodField to check image field have file or not. For example;
from rest_framework import serializers
class ItemSerializers(serializers.ModelSerializer):
brand_logo = serializers.SerializerMethodField()
class Meta:
model = Item
fields = ['id', 'brand_logo']
def get_brand_logo(self, obj):
return obj.brand.logo and obj.brand.logo.url or None
If brand has logo then simply returns it's url otherwise returns None.
Try with this code
Here get_brand_logo() return image if the image provided else return None
class ItemSerializers(serializers.ModelSerializer):
brand_logo = serializers.SerializerMethodField()
class Meta:
model = Item
fields = ['id', 'brand_logo']
def get_brand_logo(self, obj):
if obj.brand.logo:
return obj.brand.logo.url
else:None
Related
I want many to many fields to be displayed in module serializer instead of id, these are my serializers
class TrainerSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', ]
class ModuleSerializer(serializers.ModelSerializer):
trainer = serializers.CharField(source='trainer.username')
class Meta:
model = Module
fields = ['id', 'title', 'duration', 'trainer',
'publish_choice']
class Trainer(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
def __str__(self):
return str(self.user)
class Meta:
ordering = ['pk']
class Module(models.Model):
title = models.CharField(max_length=80, unique=True)
duration = models.IntegerField(verbose_name='Duration in Days/ Weeks', blank=True, null=True)
trainer = models.ManyToManyField(Trainer, blank=True)
detail = models.TextField(verbose_name='Program Details', blank=True, null=True)
notify = models.BooleanField(default=False)
publish_choice = models.CharField(verbose_name='Publish/ Draft',
max_length=80, choices=PUBLISH_CHOICES, default='publish')
and this is the error message
Got AttributeError when attempting to get a value for field trainer on serializer ModuleSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Module instance.
Original exception text was: 'ManyRelatedManager' object has no attribute 'username'.
We have a depth parameter in the serializer MetaClass. we can make use of it like below. depth=1 will retrieve all fields of a relation.
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = Module
fields = ['id', 'title', 'duration', 'trainer', 'publish_choice']
depth = 1
for reference DRF-Documentation on serializers
Its raise exception because serializers.CharField(source='trainer.username') not match ManyRelatedManager in model trainer = models.ManyToManyField(Trainer, blank=True).
If you want get all username instead of id, you can try add Custom type serialzier like this:
class ModuleSerializer(serializers.ModelSerializer):
trainer = serializers.SerializerMethodField()
def get_trainer(self, obj):
results = []
for item in obj.trainers.all():
results.append(item.username)
return results
class Meta:
model = Module
fields = ['id', 'title', 'duration', 'trainer', 'publish_choice']
trainer will return array of username relation with Module
models.py
class Userinfo(models.Model):
useruid = models.BigAutoField(db_column='UserUID', primary_key=True)
useremail = models.CharField(
db_column='UserEmail', unique=True, max_length=100)
userpassword = models.CharField(db_column='UserPassword', max_length=128)
passwordsalt = models.CharField(db_column='PasswordSalt', max_length=128)
class Meta:
managed = False
db_table = 'userinfo'
class Postinfo(models.Model):
postuid = models.BigAutoField(db_column='PostUID',primary_key=True)
useruid = models.ForeignKey(
'Userinfo', db_column='UserUID', on_delete=models.CASCADE)
content = models.TextField(db_column='Content')
class Meta:
managed = False
db_table = 'postinfo'
if i get userlist and user's last post
i think use annotate
models.Userinfo.objects.all().annotate(lastpost="??").order_by("-useruid")
what values in "??"
like this form
[{userinfo1,"lastpost":{postinfofields}},{userinfo2,"lastpost":{postinfofields}},{userinfo3,"lastpost":{postinfofields}}]
can i this query not use forloop?
Serializer.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Postinfo
fields = ('postuid','content')
class UserSerializer(serializers.ModelSerializer):
lastpost = PostSerializer()
class Meta:
model = Userinfo
fields = ['useruid', 'useremail', 'lastpost']
view.py
userinfos = models.Userinfo.objects.all().order_by("-useruid")
result = UserSerializer(userinfos,many=True)
print(result.data)
raise Exception
Got AttributeError when attempting to get a value for field `lastpost` on serializer `UserSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Userinfo` instance.
Original exception text was: 'Userinfo' object has no attribute 'lastpost'.
if i add read_only=True print this
[OrderedDict([('useuid', 1), ('useremail', 'test')]), OrderedDict([('useruid', 2), ('useremail', 'test2')])]
You can use model's property for this:
class Userinfo(models.Model):
useruid = models.BigAutoField(db_column='UserUID', primary_key=True)
useremail = models.CharField(
db_column='UserEmail', unique=True, max_length=100)
userpassword = models.CharField(db_column='UserPassword', max_length=128)
passwordsalt = models.CharField(db_column='PasswordSalt', max_length=128)
#property
def lastpost(self):
return self.postinfo_set.latest('postuid')
Now you dont need annotation, just use it for example in template like this:
{{ user.lastpost.content }}
UPD
To serialize property with ModelSerializer just add serializer's field 'lastpost':
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Postinfo
fields = ('postuid','content')
class UserSerializer(serializers.ModelSerializer):
lastpost = PostSerializer()
class Meta:
model = Userinfo
fields = ['useruid', 'useremail', 'lastpost']
UPD2
You can also implement logic directly on the serializer level, without model's property. Just use SerializerMethodField:
class UserSerializer(serializers.ModelSerializer):
lastpost = SerializerMethodField()
class Meta:
model = Userinfo
fields = ['useruid', 'useremail', 'lastpost']
def get_lastpos(self, obj):
last = obj.postinfo_set.latest('postuid')
serializer = PostSerializer(last)
return serializer.data
I'm using the DjangoRest Framework with 2 models DeviceType and Channel. Now Channel has a ForeignKeyField pointing to a DeviceType. No problem so far.
But now I don't want all DeviceTypes to be selectable when adding or editing a Channel but only the DeviceTypes that have their usesChannels field set to True.
So I used the limit_Choices_to attribute but somehow that doesn't seem to work. No matter what I do, I alway get a list with all DeviceTypes including the ones with usesChannels set to False
This is my code:
models.py
class DeviceType(models.Model):
name = models.CharField(max_length=30)
usesChannels = models.BooleanField()
def __str__(self):
return '%s' % (self.name)
class Channel(models.Model):
type = models.ForeignKey(DeviceType, limit_choices_to={'usesChannels': True})
name = models.CharField(max_length=30)
channelNr = models.IntegerField()
serializers.py
class DeviceTypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = DeviceType
fields = ('url', 'name', 'usesChannels')
class ChannelSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Channel
flieds = ('url', 'type', 'name', 'channelNr')
I used the ForeignKey.limit_choices_to example from This link
Edit: I use the DRF browsable API to add, edit and remove data.
Answer: After struggling with this for a few days I found a working solution:
models.py
class DeviceType(models.Model):
name = models.CharField(max_length=30)
usesChannels = models.BooleanField()
def __str__(self):
return '%s' % (self.name)
class Channel(models.Model):
type = models.ForeignKey(DeviceType)
name = models.CharField(max_length=30)
channelNr = models.IntegerField()
serializers.py
class DeviceTypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = DeviceType
fields = ('url', 'name', 'usesChannels')
class ChannelSerializer(serializers.HyperlinkedModelSerializer):
type = serializers.PrimaryKeyRelatedField(queryset=DeviceType.objects.filter(usesChannels=True))
class Meta:
model = Channel
flieds = ('url', 'type', 'name', 'channelNr')
Edit: For completeness I'll add the views too
Views.py:
class DeviceTypeViewSet(viewsets.ModelViewSet):
queryset = DeviceType.objects.all()
serializer_class = DeviceTypeSerializer
class ChannelViewSet(viewsets.ModelViewSet):
queryset = Channel.objects.all()
serializer_class = ChannelSerializer
Ran into this problem today. I believe it does indeed completely ignore the attribute; at least I couldn't find any reference to it in DRF's code.
So I "solved" the problem by adding Serializer.__init__ constructor and within it the following function / code:
class MySerializer(Serializers.Serializer):
def __init__(self, *args, **kwargs):
...
def limit_choices_to(field_name):
fld = self.fields[field_name]
fld.queryset = fld.queryset.filter(**Flight._meta.get_field(field_name).get_limit_choices_to())
limit_choices_to('my_field')
I have model NewsModel and 2 serializers for him:
models.py
class NewsModel(models.Model):
title = models.CharField('Заголовок', max_length=255, help_text='Максимальная длина - 255 символов')
announce = models.TextField('Анонс', help_text='Краткий анонс новости')
author = models.ForeignKey(settings.AUTH_USER_MODEL, help_text='Автор новости', related_name='news')
full_text = models.TextField('Полный текст новости', help_text='Полный текст новости')
pub_date = models.DateTimeField('Дата публикации', auto_now_add=True, default=timezone.now, help_text='Дата публикации')
def comments_count(self):
return NewsComment.objects.filter(news=self.id).count()
def get_author_full_name(self):
return self.author.get_full_name()
class Meta:
db_table = 'news'
ordering = ('-pub_date',)
serilizers.py:
from rest_framework import serializers
from .models import NewsModel
from extuser.serializers import UserMiniSerializer
class NewsReadSerializer(serializers.ModelSerializer):
author = UserMiniSerializer()
class Meta:
model = NewsModel
fields = ('id', 'title', 'announce', 'comments_count', 'reviews', 'author_name')
def get_author_full_name(self, obj):
return obj.get_author_full_name()
class NewsWriteSerializer(serializers.ModelSerializer):
def validate_author(self, value):
value = self.request.user.id
return value
class Meta:
model = NewsModel
I select serializers in the api.py:
class NewsList(ListCreateAPIView):
queryset = NewsModel.objects.order_by('-pub_date')
def get_serializer_class(self, *args, **kwargs):
if self.request.method == 'GET':
return NewsReadSerializer
return NewsWriteSerializer
class Meta:
model = NewsModel
But when I will create NewsModel item, I see Error 400: Bad request [{'author': 'This field is required'}]
How I can set current user id as NewsItem.author value on creating new item?
I don't think you're using the serializer properly. A better practice to set request related data is to override perform_create in your view:
def perform_create(self, serializer):
serializer.save(author=self.request.user)
def perform_update(self, serializer):
serializer.save(author=self.request.user)
and then set your author serializer to read-only:
author = UserMiniSerializer(read_only=True)
this way you can simply use one single NewsSerializer for both read and write actions.
In new DRF you can write
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
See http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
In DRF version prior 3 field must be declader with allow_null=True and default=None. DRF don't run checking fields without this params. Result code:
class NewsReadSerializer(serializers.ModelSerializer):
"""
Serializer only for reading.
author field serialized with other custom serializer
"""
author = UserMiniSerializer()
class Meta:
model = NewsModel
fields = ('id', 'title', 'announce', 'comments_count', 'reviews', 'author', 'pub_date',)
class NewsWriteSerializer(serializers.ModelSerializer):
"""
Serializer for creating and updating records.
author here is the instance of PrimaryKeyRelatedField, linked to all users
"""
author = serializers.PrimaryKeyRelatedField(
queryset=User.objects.all(), # Or User.objects.filter(active=True)
required=False,
allow_null=True,
default=None
)
# Get the current user from request context
def validate_author(self, value):
return self.context['request'].user
class Meta:
model = NewsModel
fields = ('title', 'announce', 'full_text', 'author',)
I would try something like this:
your models.py
class NewsModel(models.Model):
title = models.CharField(
'Заголовок', max_length=255,
help_text='Максимальная длина - 255 символов')
announce = models.TextField('Анонс',
help_text='Краткий анонс новости')
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
help_text='Автор новости', related_name='news')
full_text = models.TextField(
'Полный текст новости',
help_text='Полный текст новости')
pub_date = models.DateTimeField(
'Дата публикации', auto_now_add=True,
default=timezone.now, help_text='Дата публикации')
def comments_count(self):
return NewsComment.objects.filter(news=self.id).count()
def get_author_full_name(self):
return self.author.get_full_name()
class Meta:
db_table = 'news'
ordering = ('-pub_date',)
serializers.py
(ref.: http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault)
from <yourapp>.models import NewsModel
from rest_framework import serializers
class NewsModelSerializer(serializers.ModelSerializer):
author = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = NewsModel
Also you should set settings.py to something like this:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',)
}
How to limit images of request.user to be linked with node. I wish I could do something like:
photo = models.ForeignKey(
Image,
limit_choices_to={'owner': username},
)
but request.user rather than username and I don't want to use local threads.
models.py
class Node(models.Model):
owner = models.ForeignKey(User)
content = models.TextField()
photo = models.ForeignKey(Image)
class Image(models.Model):
owner = models.ForeignKey(User)
file = models.ImageField(upload_to=get_upload_file_name)
serializers.py
class ImageSerializer(serializers.ModelSerializer):
owner = serializers.Field('owner.username')
class Meta:
model = Image
fields = ('file', 'owner')
class NodeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Node
fields = ('content', 'photo', 'owner')
I would deal with this by overriding get_serializer_class to dynamically return a serializer class at runtime, setting the choices option on the field there:
def get_serializer_class(self, ...):
user = self.request.user
owner_choices = ... # However you want to restrict the choices
class ImageSerializer(serializers.ModelSerializer):
owner = serializers.Field('owner.username', choices=owner_choices)
class Meta:
model = Image
fields = ('file', 'owner')
return ImageSerializer
You can create a custom foreign key field and define get_queryset() method there to filter related objects to only those of your user. The current user can be retrieved from the request in the context:
class UserPhotoForeignKey(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
return Image.objects.filter(owner=self.context['request'].user)
class NodeSerializer(serializers.HyperlinkedModelSerializer):
photo = UserPhotoForeignKey()
class Meta:
model = Node
fields = ('content', 'photo', 'owner')
This example is using Django REST Framework version 3.
class CustomForeignKey(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
return Table.objects.filter(is_active=True)
class Serializer(serializers.ModelSerializer):
(...)
table= CustomForeignKey()
class Meta:
(...)
even more easy is :
class Serializer(serializers.ModelSerializer):
(...)
table = serializers.PrimaryKeyRelatedField(queryset=Table.objects.filter(is_active=True))
class Meta:
(...)
Because I am sure this logic will be used across an entire Django application why not make it more generic?
class YourPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
def __init__(self, **kwargs):
self.model = kwargs.pop('model')
assert hasattr(self.model, 'owner')
super().__init__(**kwargs)
def get_queryset(self):
return self.model.objects.filter(owner=self.context['request'].user)
serializers.py
class SomeModelSerializersWithABunchOfOwners(serializers.ModelSerializer):
photo = YourPrimaryKeyRelatedField(model=Photo)
categories = YourPrimaryKeyRelatedField(model=Category,
many=True)
# ...
from rest_framework import serializers
class CustomForeignKey(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
return Table.objects.filter(user=self.context['request'].user)
# or: ...objects.filter(user=serializers.CurrentUserDefault()(self))
class Serializer(serializers.ModelSerializer):
table = CustomForeignKey()