__init__() missing 1 required keyword-only argument: 'creator' - django

I am getting type error while setting current user to created_by field in my model
forms.py
class MyModelForm(forms.ModelForm):
class Meta:
model = Model
fields = ('name',)
def __init__(self, *args, creator, **kwargs):
super().__init__(*args, **kwargs)
self.creator = creator
def save(self, *args, **kwargs):
self.instance.created_by = self.creator
return super().save(*args, **kwargs)
views.py
class CreatEEView(LoginRequiredMixin, CreateView,):
form_class = ''
template_name = ''
success_url = ''
Models.py
class MYmodel(models.Model):
name = models.CharField()
created_by = models.ForeignKey()

You do not need to use a custom form for that because CreateView is creating ModelForm for you, what you can do is like that:
from django.db import models
class MYmodel(models.Model):
name = models.CharField()
created_by = models.ForeignKey()
Then you can override the form_valid method. This method is called when valid form data has been POSTed.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import MYmodel
class CreatEEView(LoginRequiredMixin, CreateView):
model = MYmodel
fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
You can find the same example in Django documents in this Link

Related

Django DetailView form dont save data

I have a DetailView, and need add a form to user contact.
views.py
class ProductView(FormView, DetailView):
model = Product
template_name = 'product_detail.html'
form_class = NotificationForm
success_url = '/products/'
def post(self, request, *args, **kwargs):
return FormView.post(self, request, *args, **kwargs)
forms.py
class NotificationForm(ModelForm):
..."""some fields"""
class Meta:
model = Notification
fields = [
'usernameclient',
'emailclient',
'emailclient_confirmation',
'phoneclient',
'messageclient',
]
The model Notification is where is stored the data from that form
models.py
class Notification(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
..."""some fields"""
I dont understand yet how django workflow with forms. After the submition is correct rediretc to success_url, but nothing is save in db... What is missing?
A FormView does not save the form, a CreateView does. You can save the form with:
class ProductView(FormView, DetailView):
model = Product
template_name = 'product_detail.html'
form_class = NotificationForm
success_url = '/products/'
def form_valid(self, form):
form.save()
return super().form_valid(form)

How to calculate average of some field in Django models and send it to rest API?

I want to count the average of ratings ( in Reviews model ) and send it to my API.
Models.py
from django.db import models
from adminuser.models import Categories
from accounts.models import UserAccount as User
from django.core.validators import MaxValueValidator, MinValueValidator
# Create your models here.
class Gigs(models.Model):
title = models.CharField(max_length=255)
category = models.ForeignKey(Categories , on_delete=models.CASCADE)
price = models.DecimalField(max_digits=6, decimal_places=2)
details = models.TextField()
seller = models.ForeignKey(User,default=None, on_delete=models.CASCADE)
class Reviews(models.Model):
rating = models.SmallIntegerField( default=0,validators=[MaxValueValidator(5),MinValueValidator(1)])
comment = models.CharField(max_length=500)
item = models.ForeignKey(Gigs , on_delete=models.CASCADE)
buyer = models.ForeignKey(User ,default=None, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
Views.py
from django.shortcuts import render
from .models import Gigs,Reviews
from .serializers import GigsSerializer,ReviewsSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin , RetrieveModelMixin , DestroyModelMixin, UpdateModelMixin
from rest_framework.permissions import AllowAny
# Create your views here.
#List and create (pk not required)
class GigsListAPI(GenericAPIView, ListModelMixin ):
def get_queryset(self):
username = self.kwargs['user']
return Gigs.objects.filter(seller=username)
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class GigsListCategorywise(GenericAPIView, ListModelMixin ):
def get_queryset(self):
SearchedCategory = self.kwargs['category']
return Gigs.objects.filter(category=SearchedCategory)
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class GigsListAll(GenericAPIView, ListModelMixin ):
queryset = Gigs.objects.all()
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class GigsCreateAPI(GenericAPIView, CreateModelMixin):
queryset = Gigs.objects.all()
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def post(self, request , *args, **kwargs):
return self.create(request, *args, **kwargs)
# Retrieve, update and delete (pk required)
class RUDGigsAPI(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Gigs.objects.all()
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request , *args, **kwargs):
return self.update(request, *args, **kwargs)
def put(self, request , *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def delete(self, request , *args, **kwargs):
pk = kwargs.get('pk')
p = Gigs.objects.get(id=pk)
if p.images:
p.images.delete()
return self.destroy(request, *args, **kwargs)
# VIEWS FOR REVIEWS MODEL
class ReviewsListAPI(GenericAPIView, ListModelMixin ):
def get_queryset(self):
item = self.kwargs['item']
return Reviews.objects.filter(item=item)
serializer_class = ReviewsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class ReviewsCreateAPI(GenericAPIView, CreateModelMixin):
queryset = Reviews.objects.all()
serializer_class = ReviewsSerializer
permission_classes = (AllowAny,)
def post(self, request , *args, **kwargs):
return self.create(request, *args, **kwargs)
Serializers.py
from rest_framework import serializers
from .models import Gigs, Reviews
class GigsSerializer (serializers.ModelSerializer):
class Meta:
model = Gigs
fields = ['id','title','category','price','details','seller','images']
class ReviewsSerializer (serializers.ModelSerializer):
class Meta:
model = Reviews
fields = ['id','rating','comment','item','buyer','created_at']
I want to calculate average of the ratings of some gigs or item in reviews table and then send it to API. but I am confused where to calculate it (models.py or views.py) and then how to send it to my API.
Well I am gonna explain this in details, average rating could be considered as a virtual field in Gigs, so it make sense to put it in there, so lets try that:
class Gigs(models.Model):
...
#property
def average_rating(self):
return self.reviews.aggregate(Avg('rating'))['rating_avg']
so when you gonna retrieve a single Gigs, this is good and everything, but the problem is if you need the average in the list api, this is gonna make alot of extra queries(1 for each Gig). in that case it is better to do it in bulk and in the view, so:
class GigsListAll(ListModelMixin, GenericAPIView): # you should put the mixin before the main class :D
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get_queryset(self):
return Gigs.objects.all().annotate(_average_rating=Avg('reviews__rating') # pay attention, it was annotated as _average_rating
and now we gonna change the virtual field in the model, and check if we have it precalculated, so:
class Gigs(models.Model):
...
#property
def average_rating(self):
if hasattr(self, '_average_rating'):
return self._average_rating
return self.reviews.aggregate(Avg('rating'))
finally to use it in your serializer:
class GigsSerializer (serializers.ModelSerializer):
average_rating = serializers.SerializerMethodField()
def get_average_rating(self, obj):
return obj.average_rating
class Meta:
model = Gigs
fields = ['id','title','category','price','details','seller','images','average_rating']
p.s. It is a best practice to set the related name for foreign keys, so change your reviews model like this:
class Reviews(models.Model):
...
item = models.ForeignKey(Gigs , on_delete=models.CASCADE, related_name='reviews')
First give your foreign key a name so you can reverse it:
class Reviews(models.Model):
...
item = models.ForeignKey(Gigs, on_delete=models.CASCADE, related_name='reviews')
...
Then you can do this in your serializer:
from rest_framework import serializers
from .models import Gigs, Reviews
from django.db.models import Avg
class GigsSerializer (serializers.ModelSerializer):
class Meta:
model = Gigs
fields = ['id','title','category','price','details','seller','images','avg_rating']
avg_rating = serializers.SerializerMethodField()
def get_avg_rating(self, ob):
# reverse lookup on Reviews using item field
return ob.reviews.all().aggregate(Avg('rating'))['rating__avg']
class ReviewsSerializer (serializers.ModelSerializer):
class Meta:
model = Reviews
fields = ['id','rating','comment','item','buyer','created_at']
It's redundant but I post it also here.
Objects properties have to be serialized using SerializerMethodField.
To get values for these type of fields serializers look for methodsnamed get_ and raises errors if they are not defined. The get_ method must accept an object as a parameter that the serializer use to pass in the current serialized object, so you can access its properties.
In this case it have to be:
class GigsSerializer(serializers.MethodSerializer):
average_rating = serializers.SerializerMethodField()
def get_average_rating(self, obj):
return obj.average_rating
class Meta:
model = Gigs
fields = ["""your fields here"""]

Best practice to pass attribute from ModelForm.clean() to Model.save()

class MyModel(models.Model):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if getattr(self, 'my_attr', False):
# do things
class MyForm(forms.ModelForm):
def clean(self)
cleaned_data = super().clean()
if self.has_changed():
self.instance.my_attr = self.get_the_needed_info()
return cleaned_data
class Meta:
model = MyModel
fields ='__all__'
#admin.register(MyModel)
class MyAdmin(admin.ModelAdmin)
form = MyForm
During MyModel.save(), I need to check for a condition that is evaluated in ModelForm.clean().
During clean(), I assign the attribute my_attr to self.instance.
It is working
it seems to be
thread-safe
(within an atomic transaction).
Is there any reason I miss, that urges a refactoring?
According to django docs, using the ModelForm to set an existing instance attribute, is recommended.
views.py:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(LoginRequiredMixin, CreateView):
model = Author
fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)

How to bined a detailview field to a form field in django and save into database

I am building this simple system for online voting, where people can vote but will have to pay for their vote cast.My current challenge is how to bined a selected candidate on a page and allowing voters to enter number of votes they went to cast for that candidate on the same page
I have tried using SingleObjectMixin but am having problem saving the form in database.
And also how can i prepopulate the select candidate to a model field name Nominee
model.py
class Award(models.Model):
STATUS_PUBLISHED = (
('Closed', 'Closed'),
('Opened', 'Opened'),
)
slug = models.SlugField(max_length=150)
name = models.CharField(max_length=100)
date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='award_images')
status = models.CharField(max_length=20, choices=STATUS_PUBLISHED, default='Closed')
def __str__(self):
return self.name
class Category(models.Model):
Award = models.ForeignKey(Award, on_delete=models.CASCADE)
category = models.CharField(max_length=100,)
slug = models.SlugField(max_length=150)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.category
class Nomination(models.Model):
Fullname = models.CharField(max_length=120)
Category = models.ForeignKey(Category, on_delete=models.CASCADE)
votes = models.IntegerField(default=0)
date = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=150)
image = models.ImageField(upload_to='nominations_images')
def __str__(self):
return self.Fullname
class VoteAmount(models.Model):
Nominee = models.ForeignKey(Nomination, on_delete=models.CASCADE)
votes_amount = models.IntegerField(default=0)
def __str__(self):
return self.votes_amount
views.py
from django.views.generic import ListView, DetailView, CreateView
from .models import Award, Category, Nomination, VoteAmount
from .forms import VoteAmountForm
from django.urls import reverse
from django.http import HttpResponseForbidden
from django.views.generic import FormView
from django.views.generic.detail import SingleObjectMixin
from django.views import View
class AwardView(ListView):
template_name = 'award.html'
context_object_name = 'award_list'
queryset = Award.objects.filter(status='Opened').order_by('-date')
class CategoryView(DetailView):
model = Award
template_name = 'category.html'
class NominationView(DetailView):
model = Category
template_name = 'nomination.html'
class VoteAmountView(DetailView):
model = Nomination
template_name = 'voteamount.html'
def get_context_data(self, **kwargs):
context = super(VoteAmountView, self).get_context_data(**kwargs)
context['form'] = VoteAmountForm()
return context
class AmountView(SingleObjectMixin, FormView):
template_name = 'voteamount.html'
form_class = VoteAmountForm
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
return super(AmountView, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('results', kwargs={'pk': self.object.pk})
class AuthorDetail(View):
def get(self, request, *args, **kwargs):
view = VoteAmountView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = AmountView.as_view()
return view(request, *args, **kwargs)
class PaymentView(DetailView):
model = VoteAmount
template_name = 'PaymentView.html'
form.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import VoteAmount
class VoteAmountForm(forms.ModelForm):
class Meta:
model = VoteAmount
fields = ('Nominee', 'votes_amount')
ImproperlyConfigured at /results/fiifi-quansa
AmountView is missing a QuerySet. Define AmountView.model, AmountView.queryset, or override AmountView.get_queryset().
all i went to do is show the selected condidate as a nominee field for model VoteAmount and enter my number of votes
As the error said:
AmountView is missing a QuerySet. Define AmountView.model, AmountView.queryset, or override AmountView.get_queryset().
Means, you are using SingleObjectMixin, you need to provide a queryset/model/ override get_queryset() method in the class. like this:
class AmountView(SingleObjectMixin, FormView):
template_name = 'voteamount.html'
form_class = VoteAmountForm
queryset = Nomination.objects.all()
Also, you need to override get_form_kwargs() method to load the the initial nominee data.
class AmountView(SingleObjectMixin, FormView):
template_name = 'voteamount.html'
form_class = VoteAmountForm
queryset = Nomination.objects.all()
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super().get_form_kwargs(*args, **kwargs)
form_kwargs['nominee'] = self.get_object()
return form_kwargs
And override VoteAmountForm as well to limit the choices:
class VoteAmountForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
nominee = kwargs.pop('nominee')
super().__init__(*args, **kwargs)
self.fields['Nominee'].queryset = Nominee.objects.filter(pk=nominee.pk) # <-- Please use snake_case when defining model field names(as per pep8 style guide)
self.initial['Nominee'] = nominee
class Meta:
model = VoteAmount
fields = ('Nominee', 'votes_amount')

Django getting ForeignKey objects in APIView

Hey so I am new to Django and I am writing a REST API with the following model .
# models.py
class Order(models.Model):
order_name = models.CharField(max_length=10, unique=True, default="")
def __str__(self):
return '{0}'.format(self.order_name).
class LineItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
class Meta:
unique_together = ('order', 'product')
def __str__(self):
return '{} - {}'.format(self.order.order_name, self.product.product_name).
# views.py
class OrderList(APIView):
def get(self, request):
orderlist = Order.objects.all()
serializer = OrderSerializer(orderlist, many=True)
return Response(serializer.data)
def post(self, request):
serializer = OrderSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class OrderDetail(APIView):
def get_object(self, order):
try:
return Order.objects.get(order_name=order_name)
except Order.DoesNotExist:
raise Http404
def get(self, request, order_name):
snippet = Order.objects.get(order_name=order_name)
snippet = snippet.lineitem_set.all()
serializer = OrderSerializer(snippet)
return Response(serializer.data)
So I am trying to code the OrderDetail(APIView)'s get method so that at /api/order/OrderA/ I get JSON with all the lineitems in that order. I've been struggling for a while with this now. Any suggestions?
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['order_name']
Change your serializer as below,
class LineItemSerializer(serializers.ModelSerializer):
class Meta:
model = LineItem
fields = ('id', 'product')
depth = 1
class OrderSerializer(serializers.ModelSerializer):
line_items = LineItemSerializer(many=True, source='lineitem_set')
class Meta:
model = Order
fields = ['order_name', 'line_items']
and then in your OrderDetail view as
class OrderDetail(APIView):
# your code
def get(self, request, order_name):
snippet = Order.objects.get(order_name=order_name)
serializer = OrderSerializer(snippet)
return Response(serializer.data)
DRF gives you a class to get the details of an object. Have a look here: http://www.django-rest-framework.org/api-guide/generic-views/#retrieveapiview
This an example code that should work if the order_name is passed as param in the url using a GET method.
from django.views.decorators.cache import never_cache
from django.utils.decorators import method_decorator
from rest_framework.exceptions import NotFound
from rest_framework.generics import RetrieveAPIView
# the decorator avoid to cache the object, maybe you don't need that.
#method_decorator(never_cache, name="dispatch")
class OrderDetail(RetrieveAPIView):
serializer_class = OrderSerializer
def get_object(self):
try:
return Order.objects.get(order_name=self.kwargs.get('order_name'))
except Order.DoesNotExist:
raise NotFound()