How to use Generic View for a child - django

I am starting to learn how to use the generic view. Considering that we`ve got a foreign key to parent. How can I create a child view using Generic View?
Would that be the same way we are creating the parentview?
views?.py
class ChildCreateView(generic.CreateView):
template_name = "app/create_child.html"
model = Child
form_class = ChildForm
success_url = reverse_lazy("app:index_child")
models.py
class Parent(models.Model):
pass
class Child(models.Model):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
views1.py
class ParentCreateView(generic.CreateView):
template_name = "app/create_parent.html"
model = Parent
form_class = ParentForm
success_url = reverse_lazy("app:index_parent")

You need to communicate to the child view what the parent is. The common solution is to use nested urls:
urlpatterns = [
path("/app/parents/", ParentListView.as_view()),
path("/app/parents/<int:parent_id>/", ParentDetailView.as_view()),
path("/app/parents/<int:parent_id>/children/", ChildListView.as_view()),
path("/app/parents/<int:parent_id>/children/<int:child_id>/", ChildDetailView.as_view()),
]
Now you can restrict the child to specific a parent in the ChildDetailView:
class ChildDetailView(generic.DetailView):
model = Child
pk_url_kwarg = 'child_id'
def get_queryset(self, queryset):
qs = super().get_queryset(queryset)
return qs.filter(parent__pk=self.kwargs['parent_id'])
class ChildCreateView(generic.CreateView):
model = Child
pk_url_kwarg = 'child_id'
def get_queryset(self, queryset):
qs = super().get_queryset(queryset)
return qs.filter(parent__pk=self.kwargs['parent_id'])
class ChildUpdate...
Wait, this gets repetitive:
class NestedParentMixin(generic.base.SingleObjectMixin):
parent_lookup = 'parent__pk'
parent_url_kwarg = 'parent_id'
def get_queryset(self, queryset):
qs = super().get_queryset(queryset)
filter_kwargs = {self.parent_lookup: self.kwargs[self.parent_url_kwarg]}
return qs.filter(**filter_kwargs)
class ChildDetailView(NestedParentMixin, generic.DetailView):
model = Child
pk_url_kwarg = 'child_id'
class ChildUpdateView(NestedParentMixin, generic.UpdateView):
model = Child
pk_url_kwarg = 'child_id'
class SiblingDetailView(NestedParentMixin, generic.DetailView):
model = Sibling
pk_url_kwarg = 'sibling_id'

Related

DjangoRestFramework, how to add optional field not coming from model to serializer

I have a model like this:
class Camper(models.Model):
location = models.PointField()
name = models.CharField(max_length=255)
and a viewset like this:
class CamperViewSet(viewsets.ModelViewSet):
...
def retrieve(self, request, *args, **kwargs):
"""Retrieve a Camper instance."""
show_weather = request.query_params.get('showWeather', False)
instance = self.get_object()
if show_weather:
lat = instance.location.y
lon = instance.location.x
instance.weather = getWeatherFromLatLon(lat, lon)
serializer = self.get_serializer(instance)
return Response(serializer.data)
So when I request /api/campers/8?showWeather=true I make another request in my view to get the weather from the current position.
How do I add it to my serializer ? It's an optional field so I need to manage this and it's only used in /campers/id so it will not be used in list/create/put/etc
My serializer looks like this:
class CamperSerializer(serializers.ModelSerializer):
camper_id = serializers.IntegerField(source='id')
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location')
you can add custom serializer for retrive only todo it. I called CamperRetriveSerializer.
Inside CamperRetriveSerializer, you can use SerializerMethodField for define field not have in database.
And you want check param show_weather from request, best is pass value of it to context and get it in serializer.
Like this:
class CamperRetriveSerializer(serializers.ModelSerializer):
weather = serializers.SerializerMethodField()
camper_id = serializers.IntegerField(source='id')
def get_weather(self, obj):
show_weather = self.context.get('show_weather')
if show_weather:
lat = obj.location.y
lon = obj.location.x
return getWeatherFromLatLon(lat, lon)
# define default value if not show_weather in this
return ''
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location', 'weather')
class CamperViewSet(viewsets.ModelViewSet):
...
def retrieve(self, request, *args, **kwargs):
"""Retrieve a Camper instance."""
instance = self.get_object()
show_weather = self.request.query_params.get('showWeather', False)
context = {
'show_weather': show_weather
}
serializer = CamperRetriveSerializer(instance, context=context)
return Response(serializer.data)
You can use two different serializers for this.
class CamperViewSet(viewsets.ModelViewSet):
serializer_class = CamperSerializer
def get_serializer_class(self):
serializer_class = self.serialzier_class
if self.request.method == 'GET':
serializer_class = CamperSerializerGet
return serializer_class
#Serializer for GET request
class CamperSerializerGet(serializers.ModelSerializer):
weather = serialziers.SerializerMethodField()
camper_id = serializers.IntegerField(source='id')
def get_weather(self, obj):
return obj.weather
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location', 'weather')
#For other requests call this
class CamperSerializer(serializers.ModelSerializer):
camper_id = serializers.IntegerField(source='id')
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location')

Filter TreeNode children by parent

How I can order my queryset, by category_views field in Treenode model?
I tried with ordering = ["category_stats__category_views"] (on settings "DEFAULT_FILTER_BACKENDS" configured with "rest_framework.filters.OrderingFilter") but it's, not working.
# category models.py
from tree_queries.models import TreeNode
class Category(TreeNode)
foo = models.CharFiled()
bar = models.CharField()
class CategoryStats(models.Model)
category = models.ForeingKey(Category, related_name="category_stats")
category_views = models.IntegerField()
# category views.py
class CategoryViews(FrontEndListView):
serializer_class = serializers.CategoryListSerializer
# ordering = ["category_stats__category_views"]
lookup_field = "id"
def get_queryset(self):
queryset = models.Category.objects.filter(parent_id=self.kwargs.get("id"))
for item in queryset:
return item.children.all()#.order_by("category_stats__category_views")

How to get books of the the same author in Django

I am developing an online Book Store.
Here is the models:
class Author(models.Model):
name = models.CharField(max_length=250, unique=True)
class Publisher(models.Model):
name = models.CharField(max_length=250, unique=True)
class Book(models.Model):
author = models.ManyToManyField(Author, related_name='authors')
publisher = models.ForeignKey(Publisher, on_delete=models.PROTECT, blank=True, null=True)
isbn13 = models.BigIntegerField(unique=True)
name = models.CharField(max_length=500)
...
Here is the View:
class AuthorsListView(ListView):
model = Author
context_object_name = 'authors_list'
template_name = 'authors_list.html'
paginate_by = 500
class AuthorBooksListView(ListView):
model = Book
context_object_name = 'author_books'
template_name = 'author_books.html'
def get_queryset(self, **kwargs):
author_id = Author.objects.get(pk = self.kwargs['pk'])
qs = super().get_queryset(**kwargs)
return qs.filter(author = author_id)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet
context['author'] = Author.objects.get(pk = self.kwargs['pk'])
return context
class PublishersListView(ListView):
model = Publisher
context_object_name = 'publishers_list'
template_name = 'publishers_list.html'
paginate_by = 500
class PublisherBooksListView(ListView):
model = Book
context_object_name = 'publisher_books'
template_name = 'publisher_books.html'
paginate_by = 20
def get_queryset(self, **kwargs):
publisher_id = Publisher.objects.get(pk = self.kwargs['pk'])
qs = super().get_queryset(**kwargs)
return qs.filter(publisher = publisher_id)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet
context['publisher'] = Publisher.objects.get(pk = self.kwargs['pk'])
return context
class BooksListView(ListView):
model = Book
context_object_name = 'books_list'
template_name = 'books_list.html'
paginate_by = 100
class BookDetailView(DetailView):
model = Book
template_name = 'book_detail.html'
Here is the urls:
path('authors/', AuthorsListView.as_view(), name = 'authors_list'),
path('author/<int:pk>/', AuthorBooksListView.as_view(), name='author_detail'),
path('publishers/', PublishersListView.as_view(), name='publishers_list'),
path('publisher/<int:pk>/', PublisherBooksListView.as_view(), name='publisher_detail'),
path('', BooksListView.as_view(), name='books_list'),
path('book/<int:pk>/', BookDetailView.as_view(), name='book_detail'),
On the Book Detail Page I want to display:
Related books by the same author
Related books by the same publisher
How can I do this? Please help me.
I was trying to add these code to the BookDetailView:
def get_queryset(self, **kwargs):
book = Book.objects.get(pk = self.kwargs['pk'])
queryset = {
'books': Book.objects.all(),
'publisher_books': Book.objects.filter(publisher = book.publisher.id),
}
return queryset
But it gives me an error:
'dict' object has no attribute 'filter'
#djangodjarhes - Can you try the following? Ideally I modify the get_queryset if I want to change the way the queryset should be filtered other than the URL kwargs. For anything else, I override the get_context_data
def get_context_data(self, **kwargs):
context = super(BookDetailView, self).get_context_data(**kwargs)
book = Book.objects.get(pk = self.kwargs['pk'])
publisher_books = Book.objects.filter(publisher = book.publisher.id)
context["publisher_books"] = publisher_books
return context
In your case and feel free to correct me
queryset = {
'books': Book.objects.all(),
'publisher_books': Book.objects.filter(publisher = book.publisher.id),
}
This is not right. You are returning a dict when the get_queryset is supposed to return a queryset. You cannot return a dict. Either you change it to do this
queryset = Book.objects.filter(publisher = book.publisher.id)
return queryset
or use get_context_data if you want to return a dict.

Pass data from post to Serializer

I have some data {iddepart, idarrivee} to use in serializer. This data is not present in data model but used internally to compute some fields:
Here is my code and I get error : The field 'iddepart' was declared on serializer ReservationSerializer, but has not been included in the 'fields' option.
class TravelViewReserveSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.DestroyModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
serializer_class = ReservationSerializer
permission_classes = (permissions.IsAuthenticated,)
Model = Travel
And serialiser :
class ReservationSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
#user = UserSerializer()
#travel = TravelSerializer()
iddepart = serializers.SerializerMethodField('iddepart')
idarrivee = serializers.SerializerMethodField('idarrivee')
def create(self, validated_data):
#code= random_generator()
##code = random_generator()
reservation = Reservation(**validated_data)
reservation.code = random_generator()
reservation.save()
#iddepart = validated_data['iddepart']
#idarrivee = validated_data['idarrivee']
class Meta:
model = Reservation
fields = ('id','reservedplaces','code','datecreation','travel','user')
try this
def create(self, validated_data):
iddepart = validated_data.pop('iddepart','default value')
idarrivee = validated_data.pop('idarrivee', 'default value')
# do somthing with this data
#code= random_generator()
##code = random_generator()
reservation = Reservation(**validated_data)
reservation.code = random_generator()
reservation.save()
#iddepart = validated_data['iddepart']
#idarrivee = validated_data['idarrivee']
You need to add iddepart and idarrivee to fields option in the Meta.
Your Meta should look like this,
class Meta:
model = Reservation
fields = ('id','reservedplaces','code','datecreation','travel','user',
'iddepart','idarrivee')

django rest framework view does'nt show related table data

models.py
class MEMBER(models.Model):
LB_NAME = models.CharField(max_length=40,null=True)
LB_FIRST_NAME = models.CharField(max_length=40,null=True)
DT_DAT_BIRTH = models.DateField(,null=True)
CO_ID = models.CharField(max_length=6,null=True)
class MEMBER_DECLARE_RECEPT(models.Model):
SY_MEMBER=models.ForeignKey(MEMBER,verbose_name='Name member ',null=True,related_name='Member')
DT_DECLARATION_RECEPT=models.DateField(('Date received',null=True)
serializers.py
class MemberSerializer(serializers.ModelSerializer):
class Meta:
model = MEMBER
fields=('id','LB_NAME','LB_FIRST_NAME','CO_ID')
class MemberDeclareSerializer(serializers.ModelSerializer):
class Meta:
model = MEMBER_DECLARE_RECEPT
fields=('id','SY_MEMBRE','DT_DECLARATION_RECEPT','FL_RECEPT_RECEPT_DEFAUT')
views.py
class MemberDeclareDetail(generics.ListCreateAPIView):
queryset=MEMBER_DECLARE_RECEPT.objects.all()
serializer_class =MemberDeclareSerializer
def get_object(self,pk):
try:
return self.queryset.get(pk=pk)
except MEMBER_DECLARE_RECEPT.DoesNotExist:
raise Http404
def get(self, request, pk,format=None):
entries = self.get_object(pk)
serializer = MemberDeclareSerializer(entries)
return Response(serializer.data)
result :
{
"id": 1,
"SY_MEMBER": 1,
"DT_DECLARATION_RECEPT": "2014-12-31",
"FL_RECEPT_RECEPT_DEFAUT": "1"
}
but not the related table data !
What did I miss here ?
You need Serializer relations
class MemberDeclareSerializer(serializers.ModelSerializer):
...
members = serializers.StringRelatedField(many=True)
...
Also, try to follow python naming conventions
class MemberDeclareRecept(models.Model):
member=models.ForeignKey(Member,verbose_name='Name member ',null=True,related_name='members')