I have a form in my Django project. In this form I can assign person.
This is my form:
class AssignForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=UserProfile.objects.filter(is_active=True)
label=_(u'User')
)
class Meta:
model = Customer
fields = ('user',)
I want to add another filter in this form. It is company. I get a list of all users in this form but I want to just listing the users that belongs to current user's company.
So it should be :
queryset=UserProfile.objects.filter(is_active=True, company = current_user.company)
But I cannot get requests from forms. How can I handle it?
You override the constructor of the form with:
class AssignForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=UserProfile.objects.filter(is_active=True)
label=_(u'User')
)
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user is not None:
self.fields['user'].queryset = UserProfile.objects.filter(
is_active=True, company__userprofile=user
)
class Meta:
model = Customer
fields = ('user',)
Then in the view you pass the logged in user:
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
if request.method == 'POST':
form = AssignForm(request.POST, request.FILES, user=request.user)
# …
else:
form = AssignForm(user=request.user)
# …
You have to pass the request.user from views.py in order to get it in the form. In your form write this __init__ function and pass the user from your views.py and you can get the user. You can access the user as self.user in your form
class AssignForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.get('user')
kwargs.pop('user')
In your views.py call the form like below
form = AssignForm(user=request.user)
Related
Currently I have two styles of templates: User + Client, and User + Company. And I want to create a view to create a User + account from either of these two related templates.
Currently I have achieved this, but there is a problem: the code seems to be very bloated, and I also do not know if there is CBV to edit model with related models, then it will result in other views also bloated.
Is there any way to improve this?
models.py: https://pastebin.com/9Fp0F6CG
my views.py:
from django.shortcuts import render, redirect
from django.contrib.auth import login, authenticate
from django.views import generic
from .forms import UserForm, ClientForm, CompanyForm
class ClientFormView(generic.View):
def get(self, request, *args, **kwargs):
template_name = "users/registration/form_client.html"
context = {"form_user": UserForm, "form_client": ClientForm}
return render(request, template_name, context)
def post(self, request, *args, **kwargs):
template_name = "users/registration/form_client.html"
context = {"form_user": UserForm, "form_client": ClientForm}
form_user = UserForm(request.POST)
form_client = ClientForm(request.POST)
if form_user.is_valid() and form_client.is_valid():
# get data for auth and login
email = form_user.cleaned_data["email"]
password_raw = form_user.cleaned_data["password1"]
# add user_type = client
instance_user = form_user.save(commit=False)
instance_user.user_type = "cl"
instance_user.save()
instance_client = form_client.save(commit=False)
user = authenticate(email=email, password=password_raw)
if user is not None:
# add the user in related user field
instance_client.user = user
instance_client.save()
login(request, user)
return redirect("main:home")
return render(request, template_name, context)
class CompanyFormView(generic.View):
def get(self, request, *args, **kwargs):
template_name = "users/registration/form_company.html"
context = {"form_user": UserForm, "form_company": CompanyForm}
return render(request, template_name, context)
def post(self, request, *args, **kwargs):
template_name = "users/registration/form_company.html"
context = {"form_user": UserForm, "form_company": CompanyForm}
form_user = UserForm(request.POST)
form_company = CompanyForm(request.POST)
if form_user.is_valid() and form_company.is_valid():
# get data for auth and login
email = form_user.cleaned_data["email"]
password_raw = form_user.cleaned_data["password1"]
# add user_type = client
instance_user = form_user.save(commit=False)
instance_user.user_type = "comp"
instance_user.save()
instance_company = form_company.save(commit=False)
user = authenticate(email=email, password=password_raw)
if user is not None:
# add the user in related user field
instance_company.user = user
instance_company.save()
login(request, user)
return redirect("main:home")
return render(request, template_name, context)
I have only made some improvements in regards to the views, I haven't looked at the models.
First you should change from the generic generic.View to
generic.CreateView since you are creating stuff.
When deriving from generic.CreateView you can move out the
template_name and context from the functions and put them in the
class instead, since they stay the same for both the GET and
POST operations.
Then you don't need to user the render functions, you can just call
the super that should handle the render for you.
And lastly you can create a mixin that handles the logic in the user
creation, as it's largely the same.
With the suggestions above it should look something like this. You might need to tweak it, I haven't been able to test it fully.
from extra_views import CreateWithInlinesView, InlineFormSet
class ClientCompanyMixin(object):
def create_user(self, form_user, form_second, type):
# get data for auth and login
email = form_user.cleaned_data["email"]
password_raw = form_user.cleaned_data["password1"]
# add user_type = client/company
instance_user = form_user.save(commit=False)
instance_user.user_type = type
instance_user.save()
instance = form_second.save(commit=False)
user = authenticate(email=email, password=password_raw)
return instance, user
class UserInline(InlineFormSet):
model = User
class ClientFormView(CreateWithInlinesView, ClientCompanyMixin):
template_name = "users/registration/form_client.html"
model = Client
inlines = [UserInline]
def get_context_data(self, **kwargs):
context = super(ClientFormView, self).get_context_data(**kwargs)
context["form_user"] = UserForm
context["form_client"] = ClientForm
return context
def post(self, request, *args, **kwargs):
form_user = UserForm(request.POST)
form_client = ClientForm(request.POST)
if form_user.is_valid() and form_client.is_valid():
instance_client, user = self.create_user(form_user, form_client, "cl")
if user is not None:
# add the user in related user field
instance_client.user = user
instance_client.save()
login(request, user)
return redirect("main:home")
return super(ClientFormView, self).post(request, *args, **kwargs)
class CompanyFormView(CreateWithInlinesView, ClientCompanyMixin):
template_name = "users/registration/form_company.html"
model = Company
inlines = [UserInline]
def get_context_data(self, **kwargs):
context = super(CompanyFormView, self).get_context_data(**kwargs)
context["form_user"] = UserForm
context["form_company"] = CompanyForm
return context
def post(self, request, *args, **kwargs):
form_user = UserForm(request.POST)
form_company = CompanyForm(request.POST)
if form_user.is_valid() and form_company.is_valid():
instance_company, user = self.create_user(form_user, form_company, "comp")
if user is not None:
# add the user in related user field
instance_company.user = user
instance_company.save()
login(request, user)
return redirect("main:home")
return super(CompanyFormView, self).post(request, *args, **kwargs)
I created a CBV of which I want to remove one or more fields, depending on the user. The idea is a jobsite and if the logged in user is a recruiter, than the employer field should be included, otherwise it should be excluded.
forms.py
class JobCreationForm(forms.ModelForm):
class Meta:
model = Job
# exclude = ['posted', 'provider', 'ext_id']
fields = ('title',
'job_desc',
'agency_name',
'employer',
'contact_name',
)
views.py
class JobCreateView(LoginRequiredMixin, CreateView):
template_name = 'job/job.html'
form_class = JobCreationForm
success_url = '/'
def get_context_data(self, **kwargs):
context = super(JobCreateView, self).get_context_data(**kwargs)
# import the Customers of this Company
self.fields["agency_name"].remove()
recruiter = self.request.user
self.fields["contact_name"].queryset = Profile.objects.filter(user_id = self.request.user)
# if the user is a recruiter, delete the employer field.
if Company.objects.filter(user_id = self.request.user).values('is_recruiter') == False:
pass
# self.fields.remove("employer")
del self.fields["employer"]
return context
The current error is NoneType' object has no attribute '__getitem__'.
My question: how can I remove a field from the form based on logic? I tried these versions:
self.fields["employer"].delete()
self.fields.remove("employer")
del self.fields["employer"]
Any tips?
The correct way to implement this (modify the fields of the form depending on user) is to do it on your form's __init__ method. However in order for the form to access the current user you need to pass the user to it from your view. To do this you'll use the get_form_kwargs method. Thus, start by adding the following method to your view:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs
And now, you can add an __init__ to your form like this:
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if Company.objects.filter(user_id = self.user).is_recruiter == False:
self.fields.pop("employer")
self.fields.pop('owned_by')
Notice that you first initialize the form (using super.__init__) and then you can modify the fields to your heart's content.
There are few ways to go about it.
I find having 2 separate forms RecruiterEmployeeForm and EmployeeForm may be neater.
class RecruiterEmployeeForm(forms.ModelForm):
model = Job
fields = ('title',
'job_desc',
'agency_name',
'employer',
'contact_name',
)
class EmployeeForm(forms.ModelForm):
model = Job
fields = ('title',
'job_desc',
'agency_name',
'contact_name',
)
Then you can override ger_form_class for the CBV
def get_form_class(self):
if self.request.user.is_recruiter():
return RecruiterEmployeeForm
else:
return EmployeeForm
To send extra kwargs to use generic view method get_form_kwargs and to get extra kwargs override __init__ of form and pop the extra kwargs.
forms.py
class JobCreationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(JobCreationForm, self).__init__(*args, **kwargs)
if Company.objects.filter(user_id = self.user).is_recruiter == False:
self.fields.pop("employer")
class Meta:
model = Job
# exclude = ['posted', 'provider', 'ext_id']
fields = ('title', 'job_desc', 'agency_name', 'employer', 'contact_name')
views.py
class JobCreateView(LoginRequiredMixin, CreateView):
template_name = 'job/job.html'
form_class = JobCreationForm
success_url = '/'
def get_form_kwargs(self):
kwargs = super(JobCreateView, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs
This is a part of my forms.py
class SubjectForm(forms.ModelForm):
title=forms.CharField(label='',widget=forms.TextInput(attrs={'maxlength':150,
'placeholder':'Write here. . .'}))
body=forms.CharField(label='', widget=forms.Textarea(attrs={'placeholder':'Extend here. . .'}))
board=forms.ModelChoiceField(label='',queryset=Board.objects.all(), empty_label='Select Board')
class Meta:
model = Subject
fields = ('title','body','board')
Right now it's rendering all Board objects in board form field but I want to render only those boards in which the user has subscribed. How can I get user in form and manipulate it?
forms.py:
class SubjectForm(forms.ModelForm):
title=forms.CharField(label='',widget=forms.TextInput(attrs={'maxlength':150,
'placeholder':'Write here. . .'}))
body=forms.CharField(label='', widget=forms.Textarea(attrs={'placeholder':'Extend here. . .'}))
board=forms.ModelChoiceField(label='',queryset=Board.objects.all(), empty_label='Select Board')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(SubjectForm, self).__init__(*args, **kwargs)
if user is not None:
self.fields['board'].queryset = Board.objects.filter(user=user)
class Meta:
model = Subject
fields = ('title','body','board')
views.py:
demo with CreateView:
class SubjectCreateView(CreateView):
form_class = SubjectForm
template_name = 'subject/create.html'
success_url = '/'
def get_form_kwargs(self):
kwargs = super(SubjectCreateView, self).get_form_kwargs()
if self.request.method == 'GET':
kwargs.update({
'user': self.request.user,
})
return kwargs
or
form = SubjectForm(**{'user': request.user})
I have this app where I can upload a file to a specific category or subcategory. It works fine but the problem I'm having is when I'm trying to display select values only for a specific user and for a specific parent category it just shows me all the values stored in the database.
views.py
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
login_url = reverse_lazy('users:login')
form_class = FileUploadForm
template_name = 'docman/forms/add-document.html'
success_url = reverse_lazy('docman:index')
success_message = 'Document was successfully added'
def form_valid(self, form):
profile = form.save(commit=False)
profile.user = self.request.user
return super(AddDocumentView, self).form_valid(form)
forms.py
class FileUploadForm(forms.ModelForm):
file = forms.FileField()
class Meta:
model = Document
exclude = ('user',)
fields = [
'file',
'slug',
'category',
]
def __init__(self, user=None, **kwargs):
super(FileUploadForm, self).__init__(**kwargs)
if user:
self.fields['category'].queryset = Category.objects.filter(user_id=user.id, parent_id=None)
I've tried the solutions to the similar questions which is how I even got this far, but it's still not filtering by the user and I can't figure out how to get it to filter by the parent id either. Any ideas to what I'm doing wrong? Any help is appreciated, and I can provide more information if needed.
-----------------SOLUTION UPDATE-----------------
Thanks #solarissmoke I was able to get the user information to the form. Then I just did the same thing to capture the parent_id from the url using kwargs.
views.py
# Override the view's get_form_kwargs method to pass the user and/or pk to the form:
def get_form_kwargs(self):
pk = self.kwargs['pk']
kwargs = super(AddDocumentView, self).get_form_kwargs()
kwargs['user'] = self.request.user
# Check if category exists with pk, otherwise none
if Category.objects.filter(parent_id=pk):
kwargs['pk'] = pk
else:
kwargs['pk'] = None
return kwargs
Then I added the extra agument(pk) to init
forms.py
def __init__(self, user=None, pk=None, **kwargs):
super(FileUploadForm, self).__init__(**kwargs)
if user:
self.fields['category'].queryset = Category.objects.filter(user=user, parent_id=pk)
Your form is expecting a user argument, but you aren't supplying one, so user is always None. You can override the view's get_form_kwargs method to pass the user to the form:
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
def get_form_kwargs(self):
kwargs = super(AddDocumentView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Your FileUploadForm will now get the user object and will filter results accordingly.
I'm having a problem with logged users and a Django ModelForm. I have a class named _Animal_ that has a ForeignKey to User and some data related to the animal like age, race, and so on.
A user can add Animals to the db and I have to track the author of each animal, so I need to add the request.user that is logged when the user creates an animal instance.
models.py
class Animal(models.Model):
name = models.CharField(max_length=300)
age = models.PositiveSmallIntegerField()
race = models.ForeignKey(Race)
...
publisher = models.ForeignKey(User)
def __unicode__(self):
return self.name
class AnimalForm(ModelForm):
class Meta:
model = Animal
The main goal is hide the publisher field in the form, and submit the logged user when hitting save button.
I can catch the current user in the view using initial, but what I also want is not display the field.
views.py
#login_required
def new_animal(request):
if request.method == "POST":
form = AnimalForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
variables = RequestContext(request, {'form': form})
return render_to_response('web/animal_form.html', variables)
else:
form = AnimalForm(initial={'publisher': request.user})
variables = RequestContext(request, {'form': form})
return render_to_response('web/animal_form.html', variables)
You just need to exclude it from the form, then set it in the view.
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
... and in the view:
form = AnimalForm(request.POST)
if form.is_valid():
animal = form.save(commit=False)
animal.publisher = request.user
animal.save()
(Note also that the first else clause - the lines immediately following the redirect - is unnecessary. If you leave it out, execution will fall through to the two lines at the end of the view, which are identical.)
Another way (slightly shorter):
You need to exclude the field as well:
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
then in the view:
animal = Animal(publisher=request.user)
form = AnimalForm(request.POST, instance=animal)
if form.is_valid():
animal.save()
I would add it directly to the form:
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
def save(self, commit=True):
self.instance.publisher = self.request.user
return super().save(commit=commit)
This is in my opinion the cleanest version and you may use the form in different views.
If you are using ModelAdmin
you should add method get form on your ModelAdmin
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, **kwargs)
form.request = request
return from
and you can now access request in your ModelForm
class ProductAdminForm(forms.ModelForm):
def save(self, commit: bool, *args, **kwargs):
self.instance.user = self.request.user
return super().save(commit=commit)
pass