I have an MPTT model class, and want to use it in Django-Filter.
My MPTT model is;
class ProdCategory(MPTTModel):
name = models.CharField(max_length=200, unique=True)
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
slug = AutoSlugField(populate_from='name',slugify=slugify)
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
The class is used as a foreign key in another "deal" model class as 'productCategory'
My filter class in filters.py is:
class DealFilter(django_filters.FilterSet):
class Meta:
model = deal
fields = ['store', 'productCategory']
Finally, my view is:
def filterdeals(request):
deals = deal.objects.all()
myFilter = DealFilter(request.GET, queryset = deals)
deals = myFilter.qs
args = {
"deals": deals, "myFilter":myFilter,}
return render(request, 'deals/filter.html', args)
Now, since in MPTT, I want all the children selected on filtering the parent, I want to include this code for the same:
deal.objects.filter(productCategory__in=ProdCategory.objects.get(pk=2).get_descendants(include_self=True))
But I don't know where to include this code- so that when the parent category ID is passed, I get all the children under it.
TIA for your help
Using filterset field with method will work in your case.
class DealFilter(django_filters.FilterSet):
deep_search_field = django_filters.Charfield(method='search_children', Label='Category')
class Meta:
model = deal
fields = ['store', 'productCategory', 'deep_search_field']
def search_children(self, queryset, name, value):
return queryset.filter(productCategory__in=ProdCategory.objects.get(pk=value).get_descendants(include_self=True))
when I am going to implement tag field I am getting following error
AttributeError: Got AttributeError when attempting to get a value for field tags on serializer CategorySerializers.
The serializer field might be named incorrectly and not match any attribute or key on the Category instance.
Original exception text was: 'Category' object has no attribute 'tags'.
models.py
class Category(models.Model):
name = models.CharField(max_length=100)
class Tag(models.Model):
tag_name = models.CharField(max_length=30)
class FileUp(models.Model):
name = models.ForeignKey(Category, on_delete=models.CASCADE)
file = models.FileField(upload_to='path')
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.name.name
serializers.py
class TagSerializers(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['tag_name']
class FileSerializers(serializers.ModelSerializer):
class Meta:
model = FileUp
fields = ['file']
class CategorySerializers(serializers.HyperlinkedModelSerializer):
files = FileSerializers(source='file_set', many=True, read_only=True)
tags = TagSerializers(many=True)
class Meta:
model = Category
fields = ['id', 'name', 'files', 'tags']
read_only_fields = ['tags']
def create(self, validated_data):
files_data = self.context.get('view').request.FILES
name = Category.objects.create(name=validated_data.get('name'))
for file_data in files_data.values():
FileUp.objects.create(name=name, file=file_data)
return name
here is what I tried, I have put Tag in Category model but when I am going to add files I cannot add tags to it or select tags in the admin panel. But, If I add Tag to FileUp I am getting error above shown. How can I apply to Tag to FileUp? any help please?
Use SerializerMethodField parameter,
class CategorySerializers(serializers.HyperlinkedModelSerializer):
files = FileSerializers(source='file_set', many=True, read_only=True)
tags = serializers.SerializerMethodField()
def get_tags(self, category):
return TagSerializers(Tag.objects.filter(fileup__name__categories=category), many=True).data
class Meta:
model = Category
fields = ['id', 'name', 'files', 'tags']
read_only_fields = ['tags']
How do I serialize a many-to-many field into list of something, and return them through rest framework? In my example below, I try to return the post together with a list of tags associated with it.
models.py
class post(models.Model):
tag = models.ManyToManyField(Tag)
text = models.CharField(max_length=100)
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ("text", "tag"??)
views.py
class PostViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
You will need a TagSerializer, whose class Meta has model = Tag. After TagSerializer is created, modify the PostSerializer with many=True for a ManyToManyField relation:
class PostSerializer(serializers.ModelSerializer):
tag = TagSerializer(read_only=True, many=True)
class Meta:
model = Post
fields = ('tag', 'text',)
Answer is for DRF 3
This is what I did, let´s suppose a Book can have more than one author and an Author can have more than one book:
On Model:
class Author(models.Model):
name = models.CharField(max_length=100, default="")
last_name = models.IntegerField(default=0)
class Book(models.Model):
authors = models.ManyToManyField(Author, related_name="book_list", blank=True)
name = models.CharField(max_length=100, default="")
published = models.BooleanField(default=True)
On Serializers:
class BookSerializer(serializers.ModelSerializer):
authors = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all(), many=True)
class Meta:
model = Book
fields = ('id', 'name', 'published', 'authors')
class AuthorSerializer(serializers.ModelSerializer):
book_list = BookSerializer(many=True, read_only=True)
class Meta:
model = Author
fields = ('id', 'name', 'last_name', 'book_list')
Adding to #Brian's answer
"tags": [{"name": "tag1"}] can be simplified to "tags": ["tag1", "tag2",...] in this way:
class TagListingField(serializers.RelatedField):
def to_representation(self, value):
return value.name
class PostSerializer(serializers.ModelSerializer):
tag = TagListingField(many=True, read_only=True)
class Meta:
...
More info here: https://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields
The default ModelSerializer uses primary keys for relationships. However, you can easily generate nested representations using the Meta depth attribute:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ("text", "tag")
depth = 1
As mentioned in the documentation :
The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
This works for me.
tag = TagSerializer(source="tag", read_only=True, many=True)
Django 2.0
For many to many field, if you want specific one:
class QuestionSerializer(serializers.ModelSerializer):
topics_list = serializers.SerializerMethodField()
def get_topics_list(self, instance):
names = []
a = instance.topics.get_queryset()
for i in a:
names.append(i.desc)
return names
class Meta:
model = Question
fields = ('topics_list',)
In the serializer on init method you can pass the queryset to the field and rest_framework valide the ids on that queryset
1) first extend your serializer from serializers.ModelSerializer
class YourSerializer(serializers.ModelSerializer):
2) include the field on the meta class
class YourSerializer(serializers.ModelSerializer):
class Meta:
fields = (..., 'your_field',)
3) in the init method:
def __init__(self, *args, **kwargs):
super(YourSerializer, self).__init__(*args, **kwargs)
self.fields['your_field].queryset = <the queryset of your field>
You can limit the queryset for that field under any argument using filter or exclude like normally you do. In case that you want include all just use .objects.all()
models.py
class Tag(models.Model):
name = models.CharField(max_length=100)
# ===============
# ... rest of the fields ...
class Post(models.Model):
tag = models.ManyToManyField(Tag)
text = models.CharField(max_length=100)
serialiazers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ("text", "tag")
views.py
## FUNCTION BASED VIEW
def fbvPost_ListView(request):
# list
if request.method == "GET":
posts = Post.objects.all()
serializer = PostSerializer(instance=posts, many=True)
return JsonResponse(serializer.data, safe=False)
return JsonResponse({"success": False})
# ===========================================================
## CLASS BASED VIEW
class cbvPost_ListView(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
NB: Tag, Post are two models & we need to serialize them. Here, Post model have a dependency of Tag models, so here we explicitly mention it, [tags = TagSerializer(many=True, read_only=True)] or its return it's primary field value.
DETAILS HERE
Hi I will be showing many to many for update and create. The context is the event can have many dances and dances can have many event.
The request will be as followed.
{
"competition": 2,
"title": "the title",
"dances":[ {"id":1},{"id":2}],
"description": "the desc"
}
The Create Function will be as followed.
def create(self, validated_data):
try:
dance_ids = []
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_ids.append(dance['id'])
new_event = models.Event.objects.create(**validated_data)
if dance_ids:
for dance_id in dance_ids:
new_event.dances.add(dance_id)
new_event.save()
return new_event
except Exception as e:
raise serializers.ValidationError({'detail': e})
The Update Function will be as followed.
def update(self, instance, validated_data):
# Delete all records of genres.
try:
for current_genre in instance.dances.all():
instance.dances.remove(current_genre)
# Repopulate genres into instance.
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_obj = models.Dance.objects.get(pk=dance['id'])
instance.dances.add(dance_obj)
event_updated = super().update(instance, validated_data)
return event_updated
except Exception as e:
raise serializers.ValidationError({'detail': e})
If you want to just do "dances":[1,2] instead, just make some amendments to the
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_ids.append(dance['id'])
part. I hope this will be able to help yall out! :)
First, Tag needs its own serializer too
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
Then in your PostSerializer, add one line
class PostSerializer(serializers.ModelSerializer):
tag = TagSerializer(read_only=True, many=True).data
class Meta:
model = Post
fields = ("text", "tag")
This will make it so your Tag field in Post is an array of tag ids. if you don't put the ".data" part, it will put all of the attributes of tag, which is probably too much in most cases
You can use serializers.SlugRelatedField() or serializers.StringRelatedField(many=True) Serializer relations
In your case :
class PostSerializer(serializers.ModelSerializer):
tag = serializers.StringRelatedField(many=True) # this will return a list
class Meta:
model = Post
fields = ('tag', 'text',)
I have the following code in django
models:
class Book(models.Model):
book_id = models.CharField(max_length=10, primary_key=True)
title = models.CharField(max_length=255)
class Author(models.Model):
books = models.ForeignKey(Book)
author_name = models.CharField(max_length=200)
search_fields=['author_name']
class BookAdmin(admin.ModelAdmin):
model = Book
list_display=['book_id', 'title', 'get_author']
search_fields = ['title', 'book_id']
def get_author(self, obj):
names = [a.author_name for a in obj.author_set.all()]
return names
Is there any other way to display the list of authors in book admin page. As this result is giving output in unicode
Author name
[u'Zev Halevi']
[u'Kathryn Worth', u'Dorothy Bayley']
Also i need to provide a seperate search bar for searching through the authors. I am not able to use the author_name column as this is a foreign key
use this
def get_author(self, obj):
names = "\n".join([a.author_name for a in obj.author_set.all()])
return names
I have a polling app with one of the models "Choice" consisting of 2 Foreign key fields linked to the "Person" model.
I wanted to automatically populate related "photo_other" field (with the image link) once I have selected the "name" of the person. "name" is also a Foreign Key Field linked with Choice model.
models.py
class Choice(models.Model):
name = models.ForeignKey(Person)
photo_other = models.ForeignKey(Person)
rating = models.DateTimeField(blank=True, null=True)
def __unicode__(self):
return smart_unicode(self.name)
class Person(models.Model):
name = models.CharField(max_length=250)
photo = models.ImageField(upload_to="photos")
pub_date = models.DateTimeField()
def __unicode__(self):
return smart_unicode(self.name)
Why do you want to store the same value in two different tables when they are connected through a foreign key? It just doesn't make sense.
class Choice(models.Model):
name = models.ForeignKey(Person)
rating = models.DateTimeField(blank=True, null=True)
#property
def photo_other(self):
return self.name.photo
class Person(models.Model):
name = models.CharField(max_length=250)
photo = models.ImageField(upload_to="photos")
pub_date = models.DateTimeField()
In order to make photo_other visible under the admin page of Choice model, you can do the following;
class ChoiceAdmin(admin.ModelAdmin):
list_display = ['name', 'rating', 'get_photo']
def get_photo(self, obj):
return obj.photo_other
get_photo.short_description = 'Photo'