getting all the object related to the object added when using ModelMultipleChoiceField - django

I am trying to create a sign up form where the user can specify many categories and languages from a list, but when I signup up choosing one or two objects from the list, I found that the new user is stored with all the objects. It is like I had checked all the objects.
forms.py
class SignUpForm(UserCreationForm, ModelForm):
categories=forms.ModelMultipleChoiceField(queryset=Category.objects.all(),
widget=forms.CheckboxSelectMultiple(), required=True)
languages = forms.ModelMultipleChoiceField(queryset=Language.objects.all(),
widget=forms.CheckboxSelectMultiple(), required=True)
class Meta:
model = User
fields = ['username', 'email','categories', 'languages']
models.py
class User(AbstractUser, models.Model):
password1 = models.CharField(max_length=50)
categories = models.ManyToManyField('Category')
languages = models.ManyToManyField('Language')
def __str__(self):
return self.username
class Category(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Language(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
views.py
class SignUp(CreateView):
form_class = SignUpForm
success_url = reverse_lazy('index')
template_name = 'registration/signup.html'

Related

M2M field doesn't get updated

I was trying to update an M2M field but it just doesn't get updated neither from admin panel nor the serializer!
We have a "Book" model which has a "Category" field that is M2M to BookCategory Model
this is my model:
class Book(models.Model):
#...
category = models.ManyToManyField('BookCategory', related_name='bookCategory')
def __str__(self):
return self.name
class BookCategory(models.Model):
name = models.CharField(max_length=100)
parent = models.ForeignKey('BookCategory', on_delete=models.PROTECT, null=True, blank=True)
description = models.TextField()
def __str__(self):
return self.name
and this is my view:
class BookChange(RetrieveUpdateDestroyAPIView):
serializer_class = BookSerializer
lookup_field = 'pk'
def get_queryset(self):
return Book.objects.all()
and the Model Serializer:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
The funny thing is that when I remove the current category it works but it doesn't add the new ones
I also tried Overriding the serializer.save() in def update
Can you help me out?

How do I allow only the owner of a an item to do something with it?

Could anyone explain to me how to do it? I've been trying for a while without managing to do it. I am just trying to restrict permissions so that not anyone with the right link can edit/delete a particular object
models.py:
class Items(models.Model):
author = models.ForeignKey(
'auth.User', on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=256)
description = RichTextField(blank=True, null=True)
url = models.URLField(null=True, blank=True)
image = models.ImageField(null=True, blank=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
and
views.py
class SiteList(ListView):
model = Items
class SiteUpdate(LoginRequiredMixin, UpdateView):
model = Items
form_class = SiteUpdateForm
success_url = reverse_lazy('portfolio:sitelist')
class SiteDelete(DeleteView):
model = Items
success_url = reverse_lazy('portfolio:sitelist')
You can implement a get_object() (see the inheritance hierarchy) that enforces this.
class MustBeAuthorGetObjectMixin:
def get_object(self):
obj = super().get_object()
if obj.author != self.request.user:
raise PermissionDenied("...")
return obj
# ...
class SiteUpdate(MustBeAuthorGetObjectMixin, LoginRequiredMixin, UpdateView):
# ....
This way, when UpdateView (well, BaseUpdateView) calls self.get_object(), it'll get your version which has the permission check.

modelForm got an unexpected argument 'initial'

modelForm got an unexpected argument 'initial' I am getting this error. please can anyone explain how to solve this ?
Here is my model.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, default=None)
StudentID = models.CharField(max_length=8, blank=False, unique=True)
Branch = models.CharField(max_length=255,choices=Departments,default="CSE")
def __str__(self):
return f'{self.user.username} Profile'
class complaintForm(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
category = models.CharField(max_length=255,choices=complaints,default='Mess')
title = models.CharField(max_length=20)
content = models.CharField(max_length=100)
image = models.ImageField(upload_to='complaint_pics/')
def __str__(self):
return self.title
form.py
class complaintForm(forms.ModelForm):
class Meta:
model = complaintForm
fields = ['title','content','image',]
views.py
class CreateComplaintView(CreateView):
model = complaintForm
form_class = complaintForm
template_name = 'user/post_complaint.html'
success_url = 'success'
You passed your model to the form attribute. This is why ComplaintForm is not a good idea for a model name. You better rename this to Complaint for example:
class Complaint(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
category = models.CharField(max_length=255,choices=complaints,default='Mess')
title = models.CharField(max_length=20)
content = models.CharField(max_length=100)
image = models.ImageField(upload_to='complaint_pics/')
def __str__(self):
return self.title
You will need to construct and run migrations to rename the table at the database side.
Then you thus define your form as:
from app.models import Complaint
class ComplaintForm(forms.ModelForm):
class Meta:
model = Complaint
fields = ['title','content','image',]
Finally in your CreateView, you can use:
from app.models import Complaint
from app.forms import ComplaintForm
class CreateComplaintView(CreateView):
model = Complaint
form_class = ComplaintForm
template_name = 'user/post_complaint.html'
success_url = 'success'
def form_valid(self, form):
form.instance.user = self.request.user.profile
super().form_valid(form)
Note: normally a Django models, just like all classes in Python are given a name in PerlCase, not snake_case, so it should be: Complaint instead of complaint.

Django add inlines to CreateView

I have the following admin.py
class AInlineAdmin(admin.TabularInline):
model = A
class BAdmin(admin.ModelAdmin):
fields = ['name']
list_display = ['name']
ordering = ['name']
inlines = [AInlineAdmin]
admin.site.register(B, BAdmin)
class AAdmin(admin.ModelAdmin):
fields = ['identifier']
list_display = ['identifier']
ordering = ['identifier']
admin.site.register(A, AAdmin)
And the following models.py:
class B(models.Model):
name = models.CharField(max_length=100)
def get_A(self):
return "\n".join([i.identifier for i in self.a.all()])
def __unicode__(self):
return self.name
class A(models.Model):
identifier = models.CharField(max_length=200, blank=False, default="")
c = models.ForeignKey(B, related_name='a', default=0)
def __unicode__(self):
return self.identifier
And the following views.py:
class BCreate(CreateView):
model = B
fields = ['name', 'a']
But it is not working with 'a' inside "fields = ['name', 'a']", as 'a' is not found.
How can I get inlines into the view so I could edit/delete/create A inside B view?
The CreateView does not support this. You could use django-extra-views, which comes with CreateWithInlinesView and UpdateWithInlinesView views.

Additional field while serializing django rest framework

I am a newbie to django rest framework and have created a sample Employee model.
My models.py:
class Employees(models.Model):
created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
My serializers.py:
class EmployeeSerializer(serializers.Serializer):
class Meta:
model = Employees
fields = ('first_name','last_name')
This works fine but I want an additional field full_name, which will be first_name + last_name.
How do I define this new field full_name in my serializers.py?
I see two ways here (I prefer the first way since you can reuse it in other parts of the app):
add a calculated property to your model and add it to your serializer
by using a readonly field with source=
# models.py
class Employees(models.Model):
created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
#property
def full_name(self):
return self.first_name + self.last_name
# serializers.py
class EmployeeSerializer(serializers.ModelSerializer):
full_name = serializers.Field(source='full_name')
class Meta:
model = Employees
fields = ('first_name','last_name', 'full_name')
by using SerializerMethodField
(your model unchanged)
class EmployeeSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField('get_full_name')
def get_full_name(self, obj):
return obj.first_name + obj.last_name
class Meta:
model = Employees
fields = ('first_name','last_name', 'full_name')
Provided that the Employee is a login user, then most of us will use django.auth.User, I will share how Employee can be implemented as another Profile (extension of django User). Also with the addition of full_name.read_only, first_name.write_only, and last_name.write_only
# models.py
class Employee(models.Model):
"""User Profile Model"""
user = models.OneToOneField('auth.User')
# serializers.py
class EmployeeSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.CharField(source='user.username')
email = serializers.EmailField(source='user.email')
first_name = serializers.CharField(
source='user.first_name', write_only=True)
last_name = serializers.CharField(
source='user.last_name', write_only=True)
name = serializers.CharField(
source='user.get_full_name', read_only=True)
class Meta:
model = Employee
fields = (
'url', 'username', 'email',
'first_name', 'last_name', 'name')
depth = 1
SerializerMethodField works fine, and we can also store data in serializer object and let method get_field_name use that.
Example:
class MySerializer(serializers.ModelSerializer):
statistic = serializers.SerializerMethodField()
def __init__(self, instance=None, data=serializers.empty, statistic=None, **kwargs):
super(MySerializer, self).__init__(instance=instance, data=data, **kwargs)
self.statistic = statistic
def get_statistic(self, obj):
if self.statistic is None:
return serializers.empty
return self.statistic.get(obj.id, {})