ModelMultipleChoiceField passing request.user - django

I want to use forms.ModelMultipleChoiceField in a form. I know it takes a queryset, however the query set I will be using take the param user which I normally pass in a view using request.user. However this is in a form, how do I pass request.user? do I need to?
Entry.objects.filter(request.user)

You should override your form's init method:
class MyForm(forms.ModelForm):
class Meta:
model = Entry
def __init__(self, user=None, *args, **kwargs):
super(MyForm, self).__init__(*args,**kwargs)
if user is not None:
form_choices = Entry.objects.filter(user)
else:
form_choices = Entry.objects.all()
self.fields['my_mfield'] = forms.ModelMultipleChoiceField(
queryset=form_choices
)
and in your views, when it's time to instantiate the form:
form = MyForm(request.user)
or
form = MyForm()

Related

Django: Current User Id for ModelForm Admin

I want for filter a ModelChoiceField with the current user. I found a solution very close that I want to do, but I dont understand
Django: How to get current user in admin forms
The answer accepted says
"I can now access the current user in my forms.ModelForm by accessing self.current_user"
--admin.py
class Customer(BaseAdmin):
form = CustomerForm
def get_form(self, request,obj=None,**kwargs):
form = super(Customer, self).get_form(request, **kwargs)
form.current_user = request.user
return form
--forms.py
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.filter(tenant=????))
class Meta:
model = fa_customers
How do I get the current user on modelchoice queryset(tenant=????)
How do I call the self.current_user in the modelform(forms.py)
Override __init__ constructor of the CustomerForm:
class CustomerForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['default_tax'].queryset =
fa_tax_rates.objects.filter(tenant=self.current_user))
Queryset in the form field definition can be safely set to all() or none():
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.none())
Just to sum up the solution because it was very hard for me to make this work and understand the accepted answer
In admin.py
class MyModelForm (forms.ModelForm):
def __init__(self, *args,**kwargs):
super (MyModelForm ,self).__init__(*args,**kwargs)
#retrieve current_user from MyModelAdmin
self.fields['my_model_field'].queryset = Staff.objects.all().filter(person_name = self.current_user)
#The person name in the database must be the same as in Django User, otherwise use something like person_name__contains
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user #get current user only accessible in MyModelAdminand pass it to MyModelForm
return form

Filtering a model in a CreateView with get_queryset

I'm trying to filter a model with get_queryset() and it seems to work in the view but not in the template.
My view :
class FolderCreate(CreateView):
fields = ['name', 'parent']
template_name = 'Form/folder_create.html'
def get_queryset(self):
folders = Folder.objects.filter(owner=self.request.user)
print folders # ==> return [<Folder: Folder>, <Folder: Another folder>]
return folders
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.owner = self.request.user
return super(FolderCreate, self).form_valid(form)
def get_initial(self):
if self.request.method == 'GET':
foldersUrl = self.request.META['HTTP_REFERER'].split('/')
foldersUrl.pop()
folder = urllib2.unquote(foldersUrl[-1])
try:
return {'parent' : Folder.objects.get(name=folder, owner=self.request.user)}
except Folder.DoesNotExist:
pass
As you can see, folders return two objects related to the session user in get_queryset() : 'Folder' and 'Another folder
Infortunately, the combobox of my template get all the folders, without any filtering.
Any idea ?
The issue here is that get_queryset is not used in a CreateView, as it's meant for filtering the models returned for display in a list or detail view. You want something completely different: you want to filter the choices available in a form field.
To do that you will need to create a custom ModelForm that accepts a user kwarg and filters the queryset accordingly:
class FolderForm(forms.ModelForm):
class Meta:
model = Folder
fields = ['name', 'parent']
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(FolderForm, self).__init__(*args, **kwargs)
self.fields['parent'].queryset = Folder.objects.filter(user=user)
and then change your view to use that form and pass in the user parameter:
class FolderCreate(CreateView):
template_name = 'Form/folder_create.html'
form_class = FolderForm
def get_form_kwargs(self):
kwargs = super(FolderCreate, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs

Django ModelForms __init__ kwargs create and update

I'm trying to get the request.user into a ModelForm. I feel like I've tried all permutations of this from overloading the
__init__
argument (per Django form, request.post and initial) to trying to pass it as a kwargs (per Django form __init__() got multiple values for keyword argument). I
It does seem like the kwargs is the best approach but I'm totally stymied by it.
Here's the ModelForm:
class DistListForm(ModelForm):
members = ModelMultipleChoiceField(queryset=Company.objects.none())
class Meta:
model = DistList
fields = ['name', 'description', 'members', 'is_private']
def __init__(self, *args, **kwargs):
super(DistListForm, self).__init__(*args, **kwargs)
user = kwargs.pop('user', None)
up = UserProfile.objects.get(user=user)
/.. etc ../
Here's how the create function currently works:
def distlistcreate(request):
user = {'user': request.user}
form = DistListForm(**user)
if request.method == 'POST':
form = DistListForm(request.POST)
if form.is_valid():
distlist = form.save(commit=False)
distlist.creator = request.user
distlist.save()
form.save_m2m()
return HttpResponseRedirect(reverse('distlistsmy'))
return render(request, 'distlistcreate.html',{'form':form})
which throws a TypeError: init() got an unexpected keyword argument 'user'. The update method is equally unhelpful:
def distlistupdate(request, object_id):
distlist = get_object_or_404(DistList, id=object_id)
form = DistListForm(user=request.user, instance=distlist)
if request.method == 'POST':
form = DistListForm(request.POST, user=request.user)
It also throws the same error.
I've been banging my head against this wall for two hours now. What is the correct way to pass a keyword argument into a ModelForm?
This is Django 1.6.1 if that makes a difference.
You have to pop the user argument before call super() so it will no conflict wit the default arguments of ModelForm
class DistListForm(ModelForm):
members = ModelMultipleChoiceField(queryset=Company.objects.none())
class Meta:
model = DistList
fields = ['name', 'description', 'members', 'is_private']
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(DistListForm, self).__init__(*args, **kwargs)
user_profile = UserProfile.objects.get(user=user)
Just did exactly this yesterday, on Django 1.5, and I am able to do:
def __init__(self, user, *args, **kwargs):
on my ModelForm. Then I just use user without having to pop it from the kwargs.

Django - MultipleCheckBoxSelector with m2m field - How to add object instead of save_m2m()

I use inlineformset_factory with a custom form option in order to change the queryset and the widget of a m2m field, ie: ezMap. I want the form to give the user the option to add or remove the current selected_map to the m2m field with CheckBoxSelectMultiple widget. However, I dont want to give the user the ability to remove other objects that were already there. The problem is when I save the formset with formset.save_m2m(), it overides the field and erase all objects that were already saved.
How could I just add a new object without erasing others?
models: (some of unecessary fields were removed)
class Shapefile(models.Model):
filename = models.CharField(max_length=255)
class EzMap(models.Model):
map_name = models.SlugField(max_length=50)
layers = models.ManyToManyField(Shapefile, verbose_name='Layers to display', null=True, blank=True)
class LayerStyle(models.Model):
styleName = models.SlugField(max_length=50)
layer = models.ForeignKey(Shapefile)
ezMap = models.ManyToManyField(EzMap)
forms:
class polygonLayerStyleFormset(forms.ModelForm):
add_to_map = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
self.map_selected = kwargs.pop("map_selected", None)
super(polygonLayerStyleFormset, self).__init__(*args, **kwargs)
self.fields['conditionStyle'].help_text = "Put * if you want to select the entire table"
self.fields['ezMap'].widget = forms.CheckboxSelectMultiple()
self.fields['ezMap'].queryset = EzMap.objects.filter(id=self.map_selected.id)
self.fields['ezMap'].help_text =""
class Meta:
model = LayerStyle
def save(self, *args, **kwargs):
instance = super(polygonLayerStyleFormset, self).save(*args, **kwargs)
instance.add_to_map = self.cleaned_data['add_to_map']
return instance
ftlStylePolygonFormset = inlineformset_factory(Shapefile, LayerStyle, can_delete=True, extra=1, max_num=5,
fields = ['styleName', 'conditionStyle', 'fillColor', 'fillOpacity', 'strokeColor', 'strokeWeight', 'ezMap'], form=polygonLayerStyleFormset)
views:
def setLayerStyle(request, map_name, layer_id):
map_selected = EzMap.objects.get(map_name=map_name, created_by=request.user)
layer_selected = Shapefile.objects.get(id=layer_id)
layerStyle_selected = LayerStyle.objects.filter(layer=layer_selected)
styleFormset = ftlStylePolygonFormset
if request.POST:
formset = styleFormset(request.POST, instance=layer_selected)
if formset.is_valid():
instances = formset.save()
for instance in instances:
if instance.add_to_map:
instance.ezMap.add(map_selecte)
else:
instance.ezMap.remove(map_selected)
save_link = u"/ezmapping/map/%s" % (map_name)
return HttpResponseRedirect(save_link)
else:
formset = styleFormset(instance=layer_selected)
#set initial data for add_to_map
for form in formset:
if form.instance.pk:
if map_selected in form.instance.ezMap.all():
form.fields['add_to_map'].initial = {'add_to_map': True}
I am confused as to what you're doing with the ezMap form field. You set its queryset to a single-element list, then use a CheckboxSelectMultiple widget for it. Are you setting up to let the user deselect that matching map, but not add new ones?
To do this at initialization, you need to define a custom base formset class and pass that in as the formset argument to your factory.
from django.forms.models import BaseInlineFormSet
class polygonLayerStyleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.map_selected = kwargs.pop("map_selected", None)
super(polygonLayerStyleForm, self).__init__(*args, **kwargs)
self.fields['conditionStyle'].help_text = "Put * if you want to select the entire table"
self.fields['ezMap'].widget = forms.CheckboxSelectMultiple()
self.fields['ezMap'].queryset = EzMap.objects.filter(id=self.map_selected.id)
self.fields['ezMap'].help_text =""
class Meta:
model = LayerStyle
class polygonLayerStyleFormset(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
self.map_selected = kwargs.pop("map_selected", None)
super(polygonLayerStyleFormset, self).__init__(*args, **kwargs)
def _construct_form(self, i, **kwargs):
kwargs['map_selected'] = self.map_selected
return super(polygonLayerStyleFormset, self)._construct_form(i, **kwargs)
ftlStylePolygonFormset = inlineformset_factory(Shapefile, LayerStyle, formset=polygonLayerStyleFormset, form=polygonLaterStyleForm, # and other arguments as above
)
It might be simpler to just go through the formset forms and directly change the field's queryset after creating it in your view:
formset = ftlStylePolygonFormset(instance=layer_selected)
for form in formset.forms:
form.fields['ezMap'].queryset = EzMap.objects.filter(id=map_selected.id)
Speaking of which, the usual convention is to split the POST and GET cases in the view:
from django.shortcuts import render
def setLayerStyle(request, map_name, layer_id):
map_selected = EzMap.objects.get(map_name=map_name, created_by=request.user)
layer_selected = Shapefile.objects.get(id=layer_id)
layerStyle_selected = LayerStyle.objects.filter(layer=layer_selected)
if request.method == 'POST':
formset = ftlStylePolygonFormset(request.POST, instance=layer_selected, map_selected=map_selected)
if formset.is_valid():
instances = formset.save()
save_link = u"/ezmapping/map/%s" % (map_name)
return HttpResponseRedirect(save_link)
else:
formset = ftlStylePolygonFormset(instance=layer_selected, map_selected=map_selected)
return render(request, "ezmapping/manage_layerStyle.html", {'layer_style': layerStyle_selected, 'layerStyleformset': formset, 'layer': layer_selected})
And it's better to use the redirect shortcut to reverse lookup a view for your redirect on success rather than hardcode the target URL. And to use get_object_or_404 or some equivalent when accessing objects based on URL arguments - right now a bogus URL will trigger an exception and give the user a 500 status error, which is undesirable.
To conditionally add to the ezMap relationship:
class polygonLayerStyleForm(forms.ModelForm):
add_to_map = forms.BooleanField()
def save(self, *args, **kwargs):
instance = super(polygonLayerStyleForm, self).save(*args, **kwargs)
instance.add_to_map = self.cleaned_data['add_to-map']
return instance
Then in the view:
instances = formset.save()
for instance in instances:
if instance.add_to_map:
instance.ezMap.add(map_selected)
You could also do the add call in the save method, but then you'd have to set the map as member data sometime previously - and more importantly, deal with the commit=False case.

admin auto populate user field from request

I have made a django admin form to add a new field to the model and update a generic model, my code is below. Its all working perfectly accept for saving the current logged in user. In the save() method i cannot access request.user to populate created_by field.
class EventAdminForm(forms.ModelForm):
tag_it = forms.CharField(max_length=100)
class Meta:
model = Event
# Step 2: Override the constructor to manually set the form's latitude and
# longitude fields if a Location instance is passed into the form
def __init__(self, *args, **kwargs):
super(EventAdminForm, self).__init__(*args, **kwargs)
# Set the form fields based on the model object
if kwargs.has_key('instance'):
instance = kwargs['instance']
self.initial['tag_it'] = ', '.join([i.slug for i in instance.tags.all()])
def set_request(self, request):
self.request = request
# Step 3: Override the save method to manually set the model's latitude and
# longitude properties based on what was submitted from the form
def save(self, commit=True):
model = super(EventAdminForm, self).save(commit=False)
for i in self.cleaned_data['tag_it'].split(','):
model.tags.create(slug=i, created_by=User.objects.get(username='mazban'))
if commit:
model.save()
return model
class EventForm(admin.ModelAdmin):
exclude = ('published_by', 'published_at', 'updated_at', 'updated_by', )
form = EventAdminForm
Taking from #brandon response and your comment, you can mix them doing:
# admin.py
# don't override EventAdminForm's save(). Instead implement it here:
class EventAdmin(admin.ModelAdmin):
exclude = ('published_by', 'published_at', 'updated_at', 'updated_by', )
form = EventAdminForm
def save_model(self, request, obj, form, change):
obj.save()
obj.tags.all().delete()
for i in form.cleaned_data['tag_it'].split(','):
obj.tags.create(slug=i, created_by=request.user)
To get access to the request in admin, you need to override the save_model method of your ModelAdmin:
Example:
def save_model(self, request, obj, form, change):
if not change:
obj.author = request.user
obj.save()
For more information, check the docs: https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#modeladmin-methods