Django class based views access passed arguments - django

How can I log / print passed values to class based view?
Here is my class
class ProjectCreateView(CreateView):
model = Project
form_class = ProjectForm
I have tried appending the following to the class but I'm not seeing anything printed in the console.
def get(self, request, *args, **kwargs):
logging.info(request['name'])
I can't figure out what I'm doing wrong here

Using self.args and self.kwargs works in any generic class-based-view.
class ProjectCreateView(CreateView):
model = Project
form_class = ProjectForm
def get(self, request, *args, **kwargs):
project_name = self.kwargs.get('project_name', None)
# Do something
return super(ProjectUpdateView, self).get(request, *args, **kwargs)
Looking at Classy Class Based Views, a great site for viewing the methods and attributes standard CBVs, shows us why this is. Take a look at this from the TemplateView source code:
#classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
# sanitize keyword arguments
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
The unpacked args and kwargs passed to the view method are stored as class attributes and available at any post-initialization methods.

Related

Accessing user data in a Django form class

I have a form for which I'd like to use user data to filter the content of a choicefield.
Following this solution, I added all references to user in the __init__ function of my Form class:
class MyChoiceField(forms.Form):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(MyChoiceField, self).__init__(*args, **kwargs)
user_id = self.user.id
myobjects = forms.ModelChoiceField(label='',queryset = Myobject.objects.values_list('name', flat=True).exclude(name__isnull=True).filter(Q(person__isnull=True) | Q(person=user_id)).distinct(),empty_label=None)
And in the view I call it as:
def my_view(request):
my_list = MyChoiceField(user = request.user)
context = {
'my_list': my_list,
}
return render(request, 'foo/bar.html', context)
Debugging the __init__ part indicates the queryset content is correct, but in the view, my_list contains the following:<MyChoiceField bound=False, valid=Unknown, fields=()>.
Should I include something in the form class, outside of the __init__ part for this to work?
try this
class MyChoiceField(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(MyChoiceField, self).__init__(*args, **kwargs)
user_id = user.id

Django get instance in inline form admin

Have a inline form class:
class ItemColorSelectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ItemColorSelectForm, self).__init__(*args, **kwargs)
#here i need current object
Inline class:
class ItemColorSelectInline(generic.GenericTabularInline):
model = ColorSelect
extra = 1
form = ItemColorSelectForm
Admin class
class ItemAdmin(admin.ModelAdmin):
inlines = [ItemColorInline,]
Question: how can a get current object in ItemColorSelectForm.
print kwargs return:
{'auto_id': u'id_%s', 'prefix': u'catalog-colorselect-content_type-object_id-__prefix__', 'empty_permitted': True}
Currently accepted solution is not thread safe. If you care about thread safety, never, ever assign an instance to a static class property.
Thread safe solutions are:
For Django 1.7 < 1.9 (possibly earlier versions, unclear):
from django.utils.functional import cached_property
def get_formset(self, *args, **kwargs):
FormSet = super(InlineAdmin, self).get_formset(*args, **kwargs)
class ProxyFormSet(FormSet):
def __init__(self, *args, **kwargs):
self.instance = kwargs['instance']
super(ProxyFormSet, self).__init__(*args, **kwargs)
#cached_property
def forms(self):
kwargs = {'instance': self.instance}
forms = [self._construct_form(i, **kwargs)
for i in xrange(self.total_form_count())]
return forms
return ProxyFormSet
As of Django >= 1.9 it's also possible to pass form_kwargs:
def get_formset(self, *args, **kwargs):
FormSet = super(InlineAdmin, self).get_formset(*args, **kwargs)
class ProxyFormSet(FormSet):
def __init__(self, *args, **kwargs):
form_kwargs = kwargs.pop('form_kwargs', {})
form_kwargs['instance'] = kwargs['instance']
super(ProxyFormSet, self).__init__(
*args, form_kwargs=form_kwargs, **kwargs)
return ProxyFormSet
Above solutions will make an instance kwarg available in the model form:
class InlineForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(InlineForm, self).__init__(*args, **kwargs)
print('instance', kwargs['instance'])
Solution:
Override the formset method in Inline class
def get_formset(self, request, obj=None, **kwargs):
InlineForm.obj = obj
return super(InlineAdmin, self).get_formset(request, obj, **kwargs)
To fix: currently accepted solution not safe in multi-thread mode
Arti's solution works, another better option could be:
Instead of passing the current object id into the inline form,
use the object id to create a inline form field within the get_formset().
# admin.py
class TransactionInline(admin.TabularInline):
model = Transaction
form = TransactionInlineForm
def get_formset(self, request, obj=None, **kwargs):
# comment Arti's solution
# TransactionInlineForm.project_id = obj.id
formset = super().get_formset(request, obj, **kwargs)
field = formset.form.declared_fields['purchase']
field.queryset = get_object_or_404(Project, pk=obj.id).products.all()
return formset
# forms.py
class TransactionInlineForm(ModelForm):
purchase = ModelChoiceField(queryset=None, label='Purchase', required=False)
So, there is no need to override the __init__() in form anymore, neither the current object.
works in Django 2.1.7

django object is not iterable with custom instance

I am trying to leave my object itself out of the queryset of possible options. Problem is i get the error: 'Country' object is not iterable
Not sure where i am going wrong.
My view:
def edit_country(request, country_id):
country = get_object_or_404(Country, pk=country_id)
country_form = CountryForm(instance=country)
return render(request, 'create_country.html', {'country_form': country_form})
My form init:
def __init__(self, *args, **kwargs):
super(CountryForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['likes'].queryset = Country.objects.exclude(kwargs['instance'])
self.fields['hates'].queryset = Country.objects.exclude(kwargs['instance'])
Where do i go wrong?
Change the order of the method, so you pop the kwarg first. You are sending the kwarg to super.
def __init__(self, *args, **kwargs):
instance = kwargs.pop('instance', None)
#all other stuff

Django parameter from views to forms

I want to send a parameter from the view to the form with this code.
In the view I call the constructor:
from = FormSet(request.POST or None, prefix='employee', id=id)
The id was given over the url. In the form I define the constructor like this:
class FormSet(SearchForm):
def __init__(self, *args, **kwargs):
try:
id = kwargs.pop('id')
except KeyError:
raise Http404
super(FormSet, self).__init__(*args, **kwargs)
self.fields['employee'] = ModelChoiceField(queryset=Employee.objects.all().filter(id=id))
And I got this error:
__init__() got an unexpected keyword argument 'id'
Do someone know the problem?
I had it once too, i solved it by using:
def __init__(self, id=None, *args, **kwargs):

Passing an argument to the Django ModelForm clean method

I am trying to pass an argument to the clean method of my ModelForm so that I can perform some extra validation on some data.
In my views.py file, I have:
page_data = page_form.cleaned_data(foo="bar")
In my clean_url method, I have:
def clean_url(self, **kwargs):
url = self.cleaned_data['url']
if kwargs['foo'] == url:
query = FlatPage.objects.filter(url=url)
if query.exists():
raise forms.ValidationError(("This url is already being used by the '%s' page.") % (query[0].title))
return url
I keep getting a KeyError for foo. I'm not sure where I'm making a mistake here, as I've passed kwarg variables before, but never to a clean method.
The key lies in passing the paramaters through the ModelForm's init method:
def __init__(self, *args, **kwargs):
self.url = kwargs.pop('url', None)
super(FlatPageForm, self).__init__(*args, **kwargs)
This variable can then be referenced in the clean method by calling self.url
def clean_url(self):
url = self.cleaned_data['url']
if self.url == url:
#do something
else:
#do something else
When using Class Based Views, you can use get_form_kwargs to pass the variable from the view to your form and then to you clean method:
In your view:
def get_form_kwargs(self):
kwargs = super(MyCreateView, self).get_form_kwargs()
kwargs.update({'url': self.kwargs['url']}) # or wherever the url parameter is coming from
return kwargs
In your form:
def __init__(self, *args, **kwargs):
self.url = kwargs.pop('url', None)
super(FlatPageForm, self).__init__(*args, **kwargs)
And then reference self.url in your clean() method.