I am working on creating a form that can save data to one of four tables based on the model the user picks from a different view. I don't want to create the same form for each model though.
Is there a way to pass the model name to the form when the user clicks submit?
Here is the code:
models.py
class TagModel(MP_Node):
slug = models.SlugField(max_length=64, blank=True)
name = models.CharField(max_length=64)
generic_objecttag_set = GenericRelation('ObjectTag')
def __str__(self):
return self.name
class Meta:
unique_together = (
('slug',),
)
abstract = True
class Concept(TagModel):
class Meta:
verbose_name = 'Concepts'
class Difficulty(TagModel):
class Meta:
verbose_name = 'Difficulty'
class QuestionType(TagModel):
class Meta:
verbose_name = 'Question Type'
class QuestionFormat(TagModel):
class Meta:
verbose_name = 'Question Format'
forms.py
class TagModelForm(forms.ModelForm):
def clean(self):
cleaned_data = super(TagModelForm, self).clean()
cleaned_data['slug'] = slugify(cleaned_data.get('name', ''))
return cleaned_data
class Meta:
model = models.TagModelForm
fields = ('slug', 'name',)
widgets = {
'slug': forms.HiddenInput(),
}
views.py
class TagCreateView(FormView):
form_class = forms.TagModelForm
template_name = 'tags/create.html'
#method_decorator(permission_required('tags.add_tag'))
def dispatch(self, request, *args, **kwargs):
arg_model = kwargs.get('tree_name', None)
if arg_model:
self.curr_model = get_model('tags', arg_model)
return super(TagCreateView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
data = form.cleaned_data
curr_model = self.curr_model
curr_model.add_root(**data)
return super(TagCreateView, self).form_valid(form)
def get_success_url(self):
return reverse('tags:index')
I would definitely try form inheritance and do something like:
class TagForm(forms.ModelForm):
def clean():
#...
# whatever other methods here, etc.
class ConceptTagForm(TagForm):
class Meta:
model = models.Concept
class DifficultyTagForm(TagForm):
class Meta:
model = models.Difficulty
# etc
Related
I'm working on a DRF project to learn about ContentType models.
I created a post model and comment model(ContentType) and then added comments to the post. Everything was working fine until I added django-debug-tool and duplicated queries.
I have the following questions:
I've defined a method(children) and property(total_replies) on the comment model. Since total_replies just calling children method and count the size of queryset. Will it result in hitting the database two or more times in case I use the children method in some other methods or property?
If the database is hitting multiple times, what solution two improve performance?
After adding select_related the num of queries has been reduced drastically.
Before using select_related
After using select_related
Is it good to use select_related at all places where Foreignkey has been used?
Blog app
models.py
class Post(models.Model):
title = models.CharField(verbose_name=_("Post Title"), max_length=50)
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='blog_posts')
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.title
#property
def comments(self):
instance = self
#qs = Comment.objects.filter_by_instance(instance) #before
qs = Comment.objects.select_related('user').filter_by_instance(instance)
return qs
#property
def get_content_type(self):
instance = self
content_type = ContentType.objects.get_for_model(instance.__class__)
return content_type
serializers.py
class PostSerializer(serializers.ModelSerializer):
author = UserPublicSerializer(read_only=True)
status_description = serializers.ReadOnlyField(source='get_status_display')
class Meta:
model = Post
fields = (
'url', 'id', 'title', 'author',
'content', 'category', 'total_likes',
)
class PostDetailSerializer(serializers.ModelSerializer):
author = UserPublicSerializer(read_only=True)
status_description = serializers.ReadOnlyField(source='get_status_display')
comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = (
'url', 'id', 'title', 'author', 'content',
'category', 'comments', 'total_likes'
)
def get_comments(self, obj):
request = self.context.get('request')
comments_qs = Comment.objects.filter_by_instance(obj)
comments = CommentSerializer(comments_qs, many=True, context={'request':request}).data
return comments
class PostListCreateAPIView(generics.ListCreateAPIView):
serializer_class = serializers.PostSerializer
# queryset = Post.objects.all().order_by('-id') # before
queryset = Post.objects.select_related('author').order_by('-id')
name = 'post-list'
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class PostRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = serializers.PostDetailSerializer
# queryset = Post.objects.all().order_by('-id') # before
queryset = Post.objects.select_related('author').order_by('-id')
name = 'post-detail'
permission_classes = [permissions.IsAuthenticatedOrReadOnly, account_permissions.IsStaffOrAuthorOrReadOnly]
def perform_update(self, serializer):
serializer.save(author=self.request.user)
Comment app
models.py
class CommentManager(models.Manager):
def all(self):
qs = super().filter(parent=None)
return qs
def filter_by_instance(self, instance):
content_type = ContentType.objects.get_for_model(instance.__class__)
object_id = instance.id
qs = super().filter(content_type=content_type, object_id=object_id).select_related('user').filter(parent=None)
return qs
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comments')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey(ct_field='content_type', fk_field='object_id')
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
content = models.TextField()
objects = CommentManager()
def __str__(self):
if self.is_parent:
return f"comment {self.id} by {self.user}"
return f"reply {self.id} to comment {self.parent.id} by {self.user}"
def children(self):
return Comment.objects.select_related('user').filter(parent=self)
#property
def is_parent(self):
if self.parent is not None:
return False
return True
#property
def total_replies(self):
return self.children().count()
serializers.py
class CommentSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
user = UserPublicSerializer(read_only=True)
class Meta:
model = Comment
fields = ('url', 'user', 'id', 'content_type', 'object_id', 'parent', 'content', 'total_replies',)
class CommentChildSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
user = UserPublicSerializer(read_only=True)
class Meta:
model = Comment
fields = ('url', 'user', 'id', 'content',)
class CommentDetailSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
replies = serializers.SerializerMethodField()
class Meta:
model = Comment
fields = ('url', 'id', 'content_type', 'object_id', 'content', 'replies', 'total_replies',)
def get_replies(self, obj):
request = self.context.get('request')
if obj.is_parent:
return CommentChildSerializer(obj.children(), many=True, context={'request':request}).data
return None
views.py
class CommentListAPIView(generics.ListCreateAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Comment.objects.select_related('user').order_by('-id')
name = 'comment-list'
serializer_class = serializers.CommentSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class CommentDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Comment.objects.select_related('user').all()
name = 'comment-detail'
serializer_class = serializers.CommentDetailSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Thanks in advance.
That's exactly what django docs says about select_related :
"Returns a QuerySet that will “follow” foreign-key relationships, selecting additional related-object data when it executes its query. This is a performance booster which results in a single more complex query but means later use of foreign-key relationships won’t require database queries."
They describe select_related as something complex but good in term of transactional db cost.
My models.py
from django.db import models
from django.contrib.auth.models import User
import datetime
from django.utils import timezone
# Create your models here.
class LiveClass(models.Model):
standard = models.IntegerField()
no_of_students_registered = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'Class'
def __str__(self):
return str(self.standard) + ' class'
class User_details(models.Model):
name = models.OneToOneField(User, on_delete = models.CASCADE, max_length=30)
standard = models.ForeignKey(LiveClass, on_delete=models.CASCADE)
email = models.EmailField(max_length=30)
mobile_number = models.IntegerField()
class Meta:
verbose_name_plural = 'User_details'
def __str__(self):
return self.name
class Mentor(models.Model):
name = models.CharField(max_length=30)
details = models.TextField()
ratings = models.FloatField(default=2.5)
class Meta:
verbose_name_plural = 'Mentors'
def __str__(self):
return self.name
class LiveClass_details(models.Model):
standard = models.ForeignKey(LiveClass, on_delete=models.CASCADE)
chapter_name = models.CharField(max_length=30)
chapter_details = models.TextField()
mentor_name = models.ForeignKey(Mentor, max_length=30, on_delete=models.CASCADE)
class_time = models.DateTimeField()
end_time = models.DateTimeField()
isDoubtClass = models.BooleanField(default=False)
doubtsAddressed = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'LiveClass_details'
def __str__(self):
return self.chapter_name
class SavedClass(models.Model):
class_details = models.ForeignKey(LiveClass_details, on_delete=models.CASCADE)
name = models.ForeignKey(User_details, on_delete=models.CASCADE)
is_registered = models.BooleanField(default=False)
is_attended = models.BooleanField(default=False)
class Meta:
verbose_name_plural = 'SavedClasses'
def __str__(self):
return 'SavedClass : ' + str(self.class_details)
my serializers.py
from rest_framework import serializers
from . import models
class LiveClass_serializer(serializers.ModelSerializer):
class Meta:
model = models.LiveClass
fields = '__all__'
class User_details_serializer(serializers.ModelSerializer):
class Meta:
model = models.User_details
fields = '__all__'
class LiveClass_details_serializer(serializers.ModelSerializer):
class Meta:
model = models.LiveClass_details
fields = '__all__'
class Mentor_serializer(serializers.ModelSerializer):
class Meta:
model = models.Mentor
fields = '__all__'
class SavedClass_serializer(serializers.ModelSerializer):
class Meta:
model = models.SavedClass
fields = '__all__'
my views.py
from django.shortcuts import render
from rest_framework import mixins
from rest_framework import generics
from django.contrib.auth.mixins import LoginRequiredMixin
from rest_framework import status
from django.contrib.auth.models import User
from rest_framework.response import Response
from . import serializers
from . import models
# Create your views here.
class ListLiveClass(mixins.ListModelMixin, LoginRequiredMixin, generics.GenericAPIView):
queryset = models.LiveClass_details.objects.all()
serializer_class = serializers.LiveClass_details_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class LiveClassView(mixins.ListModelMixin,
mixins.CreateModelMixin,
LoginRequiredMixin,
generics.GenericAPIView):
queryset = models.LiveClass_details.objects.all()
serializer_class = serializers.LiveClass_details_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if request.user.is_superuser:
return self.create(request, *args, **kwargs)
else:
return Response(status=status.HTTP_403_FORBIDDEN)
class LiveClassViewId(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
LoginRequiredMixin,
generics.GenericAPIView):
queryset = models.LiveClass_details.objects.all()
serializer_class = serializers.LiveClass_details_serializer
lookup_field = 'id'
def get(self, request, id=None, format=None):
if id:
return self.retrieve(request)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
def put(self, request, id, format=None):
if request.user.is_superuser:
return self.update(request, id)
else:
return Response(status=status.HTTP_403_FORBIDDEN)
def delete(self, request, id, format=None):
if request.user.is_superuser:
return self.destroy(request, id)
else:
return Response(status=status.HTTP_403_FORBIDDEN)
class ListMentors(mixins.ListModelMixin, LoginRequiredMixin, generics.GenericAPIView):
queryset = models.Mentor.objects.all()
serializer_class = serializers.Mentor_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class ListUserDetails(mixins.ListModelMixin, LoginRequiredMixin, generics.GenericAPIView):
queryset = models.User_details.objects.all()
serializer_class = serializers.User_details_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
#api endpoints to save and register live classes
class SavedClassView(LoginRequiredMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
model = models.SavedClass
serializer = serializers.SavedClass_serializer
def get_object(self):
return self.request.user.id
def get(self, request):
CurrentUserID = self.get_object()
In SavedClassView i want to get saved classes from the current logged user only and i want to preserve this user from getting other user saved classes , i have done something like above but i am getting no perfect logic that fits the working of django rest framework , please help me in achieving the above result
Add filter to queryset
queryset = models.User_details.objects.filter(user=request.user)
Just make sure to implement tokens so you can access request.user
how in forms.py class, overwrite the default value of the model category, without adding it to the html form?
forms.py
class FastForm(forms.ModelForm):
class Meta:
model = Orders
fields = ['device']
models.py
class Orders(models.Model):
device = models.CharField(max_length=150)
category = models.ForeignKey('Category', default=1, on_delete=models.PROTECT)
class Category(models.Model):
category = models.CharField(max_length=150, db_index=True)
UPD
views
class OrderAddView(TemplateView):
template_name = 'orders/order_add.html'
def get(self, request, *args, **kwargs):
context = super().get_context_data(**kwargs)
formOne = FastForm
context.update({'formOne': formOne})
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
formOne = FastForm(self.request.POST)
if formOne.is_valid():
form_update = formOne.save(commit=False)
form_update.save()
return HttpResponseRedirect('orders_home')
else:
print('NotValid')
id field and name field not showing in result.
in models.py:
class Group(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
admin = models.ForeignKey(User, on_delete=models.CASCADE)
member = models.ManyToManyField(User, related_name='groups_user')
def __str__(self):
return self.name
in serializers.py:
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','first_name', 'last_name')
class GroupSerializer(serializers.Serializer):
admin = SimpleUserSerializer()
class Meta:
model = Group
fields = ('id','name','admin')
views.py:
#api_view(['GET'])
#permission_classes((IsAuthenticated,))
def getSomeGroup(request):
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
randomGroup = Group.objects.filter(id__in=randomGroupId)
serializer = GroupSerializer(randomGroup, many=True)
#print(serializer)
return Response(serializer.data)
the result comes like this:
[{"admin":{"id":1,"first_name":"asif","last_name":""}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}}]
why id and name field not showing?
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
First try to access all admin
#api_view(['GET'])
#permission_classes(IsAuthenticated)
def getSomeGroup(request):
randomGroup = Group.objects.all()
serializer = GroupSerializer(randomGroup, many=True)
return Response(serializer.data)
If that works there may be issue in your these two line
The Issue may be in these two lines
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
Modify serializers.py:
class GroupSerializer(serializers.ModelSerializer):
I am a beginner in Django. Right now, I am working with the APIs. I am facing a problem. I can't view the string value one of the fields, called label, at http://127.0.0.1:8000/gameapi/. Instead of seeing "label_tag": "racing", I am seeing "label_tag": [2]. Here is the screenshot:
Here are my codes of serializers.py located inside gamreview folder.
from rest_framework import serializers
from .models import Game, Tags
# class TagSerializer(serializers.ModelSerializer):
# class Meta:
# model = Tags
# fields = ['label']
class GameSerializer(serializers.ModelSerializer):
# label_tag = TagSerializer(many=True)
class Meta:
model = Game
# fields = ['id', 'title', 'developer', 'platform']
fields = ['id', 'title', 'developer', 'platform','label_tag']
def create(self, validated_data):
label_tag_data = validated_data.pop('label_tag')
game = Game.objects.create(**validated_data)
for tags_data in label_tag_data:
Tags.objects.create(game=game, **tags_data)
return Game.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.developer = validated_data.get('developer', instance.developer)
instance.platform = validated_data.get('platform', instance.platform)
# instance.tag = TagSerializer(read_only=True, many=True)
instance.save()
return instance
Here are my codes of models.py under gamreview folder:
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Tags(models.Model):
label = models.CharField(max_length=20)
def __str__(self):
return self.label
class Game(models.Model):
title = models.CharField(max_length=100)
developer = models.CharField(max_length=100)
platform = models.CharField(max_length=50, default='null')
label_tag = models.ManyToManyField(Tags)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
class Review(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
review = models.CharField(max_length=1000)
date = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.game
Here are my codes of views.py under gamreview folder:
from django.views import generic
from .models import Game
from rest_framework import generics
from .serializers import GameSerializer
# Create your views here.
class GameListView(generic.ListView):
template_name = 'gamereview/gamelist.html'
context_object_name = 'all_games'
def get_queryset(self):
return Game.objects.all()
class ReviewView(generic.DetailView):
model = Game
template_name = 'gamereview/review.html'
# class GameApiView(generics.ListAPIView):
class GameApiView(generics.ListCreateAPIView):
queryset = Game.objects.all()
serializer_class = GameSerializer
class GameDetailApiView(generics.RetrieveUpdateDestroyAPIView):
queryset = Game.objects.all()
serializer_class = GameSerializer
Here are my codes of urls.py under gamreview folder:
from . import views
from django.urls import path
app_name = 'gamereview'
urlpatterns = [
path('gamereview/', views.GameListView.as_view(), name='gamelist'),
path('gamereview/<slug:slug>/', views.ReviewView.as_view(), name='review'),
path('gameapi/', views.GameApiView.as_view(), name='gamelistapi'),
path('gameapi/<int:pk>/', views.GameDetailApiView.as_view()),
]
I don't get any error while running the server. However, "label_tag": "racing"is not showing up.
How can I fix the issue?
in Game model change label_tag to
label_tag = models.ManyToManyField(Tags, related_name='tags')
this can be considered as tags is a field for a Game model
your serializer classes should be
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tags
fields = ['label']
class GameSerializer(serializers.ModelSerializer):
tags= TagSerializer(many=True)
class Meta:
model = Game
# fields = ['id', 'title', 'developer', 'platform']
fields = ['id', 'title', 'developer', 'platform','tags']
you will get tags as a list of tags objects every tag have a label field with string value
'tags': [
{
'label': 'etc'
},
{
'label': 'etc2'
}
]
if you want tags like
'tags': [
'etc',
'etc'
]
this code will help you
```python
class GameSerializer(serializers.ModelSerializer):
label_tags = serializers.SerializerMethodField()
class Meta:
model = Game
# fields = ['id', 'title', 'developer', 'platform']
fields = ['id', 'title', 'developer', 'platform','label_tags']
def get_label_tags(self, game):
new_tags = []
tags = game.label_tag.all()
for tag in tags:
new_tags.append(tag.label)
return new_tags
You can use the source argument
class GameSerializer(serializers.ModelSerializer):
label_tag = serializers.ReadOnlyField(source='label.label')
class Meta:
model = Game
fields = ['id', 'title', 'developer', 'platform','label_tag']
another way is to use a SerializerMethodField like this(it's an overkill in this case I believe)
class GameSerializer(serializers.ModelSerializer):
label_tag = serializers.SerializerMethodField()
class Meta:
model = Game
fields = ['id', 'title', 'developer', 'platform','label_tag']
def get_label_tag(self, obj):
return obj.label.label
check similar answers here DRF source argument