Passing an argument to the Django ModelForm clean method - django

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.

Related

Attibutes are 'disappearing' from CBV. Django

I am trying to build CBV with class View parent. This view takes slug of object and find that object between two django models. The functions from services.py was doing a lot of DB queries, so, I tried to reduce them by giving to FeedbackSection necessary attributes(slug, model_instance and context) and lately override them in get method.
class FeedbackSection(View):
"""
Feedback section for different objects.
This is something like 'generic' view, so I implement it that it will find
the model and feedbacks for this model by having only slug.
"""
template_name = 'feedbacks/feedback-section.html'
form_class = CreateFeedbackForm
slug = None
model_instance = None
context = None
def get(self, request, *args, **kwargs):
self.slug = kwargs.get('slug')
self.model_instance = get_model_instance(self.slug)
self.context = get_feedback_section_context(self.slug, self.form_class, self.model_instance)
return render(request, self.template_name, self.context)
#method_decorator(login_required)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# will create feedback object and update model[Advert, Company] rating.
end_feedback_post_logic(self.request.user, form, self.model_instance)
return render(request, self.template_name, self.context)
The attributes(slug, model_instance and context), when post method is in runtime are equivalent to None.
The problem is that this implementation was working fine yesterday, but today it's not.
I know I can use my functions again, but in post method. I don't want to do this. Because it will multiple DB Queries by two.
We need to override the setup method of the View class and define those attributes there.
def setup(self, request, *args, **kwargs):
self.slug = kwargs.get('slug')
self.model_instance = get_model_instance(self.slug)
self.context = get_feedback_section_context(
self.slug,
self.form_class,
self.model_instance
)
return super().setup(request, *args, **kwargs)

Passing kwargs from CBV to Form in Django

I have a ModelForm which needs a user passed in so that the queryset can be updated. I am overriding the __init__ method of the ModelForm as such:
def __init__(self, *args, **kwargs):
# override init to get user's casino's EmployeeType queryset
self.user = kwargs.pop('user')
print(self.user)
super(MemoForm, self).__init__(*args, **kwargs)
self.fields['receiver'].queryset = EmployeeType.objects.filter(
casino=self.user.casino
)
In the View I have a get and a post method. I am trying to pass the **kwargs in as such:
class VideoUploadView(LoginRequiredMixin, View):
"""
Display a form for uploading videos.
"""
form_class = VideoUploadForm
success_url = '/videos'
template_name = 'videos/video_upload.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(
request,
self.template_name,
{'form': form, 'user': self.request.user}
)
In a CreateView you are able to use the get_form_kwargs method to pass in the **kwargs. How is it done in a normal View? Should we use the __init__ method? The way shown above does not seem to work as both *args and **kwargs seem to be empty.
These are the built-in methods of View.
I don't really understand why you're not using a FormView here as well, so that you can still override get_form_kwargs; you really shouldn't ever need to define get (or post) directly.
But nevertheless, the answer is simple: you just pass your kwargs directly to the form:
form = self.form_class(user=request.user)

Django class based views access passed arguments

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.

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):