Custom Query for Django ModelChoiceField using user.get_profile() - django

I am wondering how to access a user's profile when creating the
queryset for a ModelChoiceField. I would like to be able to use the
ModelChoiceField to display contacts from another table based on a
parameter saved in the user profile i.e.
who = forms.ModelChoiceField(queryset=Contacts.objects.filter(id__exact=request.user.get_profile().main_company))
Is there a better way to do this (beyond an ajax picker in the
template)?
Greg

For Those interested I was able to come up with a solution from the following SO discussions:
How do I access the request object or any other variable in a form's clean() method?
Django: accessing the model instance from within ModelAdmin?
class InspectionRequestForm(ModelForm):
....
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(InspectionRequestForm, self).__init__(*args, **kwargs)
companyid = self.request.user.get_profile().main_contactnum.clientid.idflcustomernum
self.fields['who'].queryset = Contacts.objects.filter(clientid__exact=companyid)
My View:
Save Form (Not as necessary to include request=request here, but just in case)
form = InspectionRequestForm(request.POST, request=request)
Or Empty Form
form = InspectionRequestForm(request=request)
Thanks to Daniel Roseman for both of the previous answers.
https://stackoverflow.com/users/104349/daniel-roseman

Related

What is the Django way to add data to a form that is already instantiated?

Suppose you have a ModelForm to which you have already binded the data from a request.POST. If there are fields of the ModelForm that I don't want the user to have to fill in and that are therefore not shown in the form (ex: the user is already logged in, I don't want the user to fill a 'author' field, I can get that from request.user), what is the 'Django' way of doing it ?
class RandomView(View):
...
def post(self, request, *args, **kwargs):
form = RandomForm(request.POST)
form.fill_remaining_form_fields(request) ### How would you implement this ???
if form.is_valid():
...
I have tried adding the fields to the form instance (ex: self.data['author'] = request.user) but given its a QueryDict it is immutable so it clearly isn't the correct way of doing this.
Any suggestions ?
My bad, the Django documentation actually explains how to do this in https://docs.djangoproject.com/en/4.0/topics/forms/modelforms/#selecting-the-fields-to-use (see the Note).

Can't get dynamic python ModelChoiceField to work

I'm new to python and trying to understand how to get a dynamic ModelChoiceField to work. It works fine when I select an object with all but I'm trying to get the dropdown to reflect a user's attribute. Here is my code:
Forms.py
class ViewByMake(forms.Form):
dropdown = forms.ModelChoiceField(queryset=Make.objects.none())
def __init__(self, user, *args, **kwargs):
user = kwargs.pop('user')
super(ViewByMake, self).__init__(*args, **kwargs)
qs = Make.objects.filter(user=user)
self.fields['dropdown'].queryset = qs
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
Views.py
def view_bymake(request):
form = ViewByMake(request.POST or None, user=request.user)
if request.method == 'POST':
if form.is_valid():
make = form.cleaned_data['dropdown']
return HttpResponseRedirect(make.get_absolute_url1())
return render(request,'make/view_make.html',{'form':form})
This code works fine if I remove all user= references but then only returns the full make objects list which is not what I want. I found a very similar question on StackOverflow, but when I duplicated the code identically, it still doesn't work and it is giving me the following error:
init() got multiple values for argument 'user'
I searched the end of the internet on this topic. I'm open to other ideas if I'm approaching this poorly. I'm trying to basically get a filtered list based on criteria associated with a user's profile. I definitely need the drop down field to be specific to a user based on a profile setting. Thanks for your help in advance. I'm running django 1.11.2 and Python 3.6.1.
This is the updated model which need to include the user attribute which I didn't realize that I had to specify:
class Make(models.Model):
name = models.CharField(max_length=264,unique=True)
user = models.ForeignKey(User,null=True,on_delete=models.CASCADE)
Try with request, send request from form and get request in init method of form
views.py
def view_bymake(request):
form = ViewByMake(request.POST or None, request=request)
forms.py
def __init__(self, user, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(ViewByMake, self).__init__(*args, **kwargs)
qs = Make.objects.filter(user=self.request.user)
self.fields['dropdown'].queryset = qs
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
The answer to my original question, how do I get user=user to work consists of making sure that your form, view, and model all reference user. I originally had the user reference in the view and the form correct, but I neglected to make sure user= was specified on the model I was referencing. I thought it was built in, but turns out you have to specifically reference it on your model. I'm new at this so it was a learning experience. On to the next challenge!

How to make an admin panel to review the entries per field in Django?

I am working on making an application in which users can submit entries and each submitted entry is reviewed by site admin.
Each entry has many fields and some fields can be accepted and some fields can be rejected.
For adding this functionality in admin, I added a custom view to
admin at '/review' url. Now in this view I want to create the
functionality to accept or reject a field, so that when the form is
submitted, I can take further actions based on that.
Till now i am able to render the modelform.
Now I want something like checkbox per field to mark a field as accepted or rejected, based on which I can take action in view. One way will be to create a form and then create BooleanField for each field manually.
How to accomplish this in DRY way ?
If I got you right, your particular question is about constructing a form with checkboxes for all the model's fields. Here's the way to do it:
class ReviewForm(forms.Form):
def __init__(self, model, *args, **kwargs):
super(ReviewForm, self).__init__(self, *args, **kwargs)
for field in model._meta.get_fields():
self.fields[field.name] = forms.BooleanField(label=field.name, initial=True)
def triage_view(request, *args, **kwargs):
form = ReviewForm(Entry, data=request.POST or None)
# do stuff with that form, for example
accepted_field_names = [key for key, val in form.cleaned_data if val]

Cannot validate dynamic choices with Django ModelForm

I have a Django ModelForm in Google App Engine with a ChoiceField, let's say location:
class MyForm(ModelForm):
location = ChoiceField(label="Location")
class Meta:
model = MyModel
In order to dynamically add the choices for location, and not have issues with app caching, I add them after the form has initialized:
form = MyForm(request.POST, instance=my_instance)
form.fields['location'].choices = Location.all().fetch(1000)
The problem I'm having now is that when the form is initialized via the data in request.POST the choices do not yet exist and I am receiving an error stating that an invalid choice is made (since the value does not yet exist in the list of choices).
I don't like that validation is occurring when I am initializing the form instead of waiting until I call form.is_valid(). Is there any way to suppress validation during my object instantiation? Or some other way to fix this?
UPDATE: I'm pretty sure ModelFormMetaclass is causing me my grief by validating the provided instance when the form is created. Still not sure how to fix though.
Thanks!
There must be other ways to do this, but possibly the most straightforward is to add the field in the form's __init__() method:
class MyForm(ModelForm):
...
def __init__(self, *args, **kwargs):
try:
dynamic_choices = kwargs.pop('dynamic_choices')
except KeyError:
dynamic_choices = None # if normal form
super(MyForm, self).__init__(*args, **kwargs)
if dynamic_choices is not None:
self.fields['location'] = ModelChoiceField(
queryset=dynamic_choices)
class Meta:
model = MyModel
And your view would look something like:
def my_view(request):
locations = Location.objects.all() # or filter(...) or whatever
dynamic_form = MyForm(dynamic_choices=locations)
return direct_to_template(request,
'some_page.html',
{'form': dynamic_form},)
Let us know how that works for you.

Readonly fields in the django admin/inline

I use this snippet to show several fields in my admin backend as readonly, but as noticed in the comments, it does not work on stackedinline/tabularinline. Is there any other way to achieve this? I have a list of objects attached to a model and just want to show it in the model's details view without the possibility to change values.
If you are running Django 1.3 or later; there's an attribute named ModelAdmin.readonly_fields which you could use.
InlineModelAdmin inherits from ModelAdmin, so you should be able to use it from your inline subclass.
I've encountered the same problem today. Here is my solution. This is example of read-only field for the ForeignKey value:
class MySelect(forms.Select):
def render(self, name, value, attrs=None, choices=()):
s = Site.objects.get(id=value)
return s.name
class UserProfileInlineForm(forms.ModelForm):
site = forms.ModelChoiceField(queryset=Site.objects.all(), widget=MySelect)
class UserProfileInline(admin.StackedInline):
model = UserProfile
form = UserProfileInlineForm
As is the case with JQuery, it seems you can achieve this by changing an attr called "disabled" (works in my Safari, OK we're now in 2013 :-) ).
Example below:
def get_form(self, request, obj=None, **kwargs):
result = super(<your ModelAdmin class here>, self).get_form(request, obj=obj, **kwargs)
result.base_fields[<the select field you want to disable>].widget.attrs['disabled'] = 'disabled'
return result