Django-contact-forms: how to subclass and supply 'initial' parameter? - django

I have a Django form that is subclassed from the django-contact-form application. I want to supply an initial parameter to the form (which will vary depending on context).
This code returns the contact form fine, but obviously doesn't supply an initial parameter, so I need to extend it:
def contact_form(request):
scraper_form = scraperContactForm
return contact_views.contact_form(request=request, form_class=scraper_form)
This attempt to supply an initial parameter fails:
def contact_form(request):
scraper_form = scraperContactForm(initial={'title' : 'hello world'})
return contact_views.contact_form(request=request, form_class=scraper_form)
TypeError at /contact/
Keyword argument 'request' must be supplied
Hence, I have tried to supply a request argument, but weirdly, that fails by saying the form object is not callable:
def contact_form(request):
scraper_form = scraperContactForm(request=request, initial={'title' : 'hello world'})
# supply initial subject_dropdown field, if there's anything in the subject_type
return contact_views.contact_form(request=request, form_class=scraper_form)
TypeError at /contact/
'scraperContactForm' object is not callable
And no matter how I try to supply the request parameter, I keep on getting 'scraperContactForm' object is not callable.
FYI, this is the code for my subclassed form:
class scraperContactForm(ContactForm):
subject_dropdown = django.forms.ChoiceField(label="Subject type", choices=(('suggestion', 'Suggestion for improvement'), ('bug', 'Report a bug'), ('other', 'Other')))
title = django.forms.CharField(widget=django.forms.TextInput(), label=u'Subject')
recipient_list = [settings.FEEDBACK_EMAIL]
Please can anyone suggest what's going wrong?

You don't supply the full traceback in any of your examples. If you had, I suspect we would see that it's not your code that's giving the 'scraperContactForm' object is not callable error, but the subsequent call to the main contact_form view.
This is because that view is clearly expecting a form class, as indicated by the keyword parameters. However, you're already instantiating the form, by calling it in your first line, so you're actually passing a form instance, which isn't callable.
I would suggest that if you want to provide an initial value for a field, you do so in your subclassed form's definition:
class scraperContactForm(ContactForm):
title = django.forms.CharField(initial='hello world')

Related

Custom admin form error "need more than 1 value to unpack"

I have been adapting this article How to Add Custom Action Buttons to Django Admin to my particular circumstance and things are working pretty well except for this one strange error I am getting. In my forms.py I have defined class DenyForm: (Note the request parameter in form_action is not the usual API request. I have a model class Request.) What should form_action be returning?
class DenyForm(ApproveOrDenyForm):
def form_action(self, request, admin_approver ):
print "forms DenyForm Called."
justification = self.cleaned_data['justification']
request.admin_deny(admin_approver=admin_approver,justification=justification)
return [request]
#return request.admin_deny(admin_approver=admin_approver,justification=justification)
This results in a message of
“‘Please correct the errors below.’ need more than 1 value to unpack”.
I have tried different endings to my form_action method. If my last line is:
return request
I get “‘Please correct the errors below.’ ‘Request’ object is not iterable”.
If the last line is:
return request.admin_deny(admin_approver=admin_approver,justification=justification)
I get this … “‘Please correct the errors below.’ ‘NoneType’ object is not iterable”. That is because admin_deny() does not return anything. What should form_action be returning?
Update: Here is the code that call form_action():
class ApproveOrDenyForm(forms.Form):
justification = forms.CharField(
required=True,
widget=forms.Textarea,
)
def save(self, req, login ):
try:
user = User.objects.filter(login=login).get()
req, action = self.form_action(req, user )
except Exception as e:
error_message = str(e)
self.add_error(None, error_message)
raise
return req, action
When you call form_action you are expecting it to return an iterable of two items - req and action. So you need to ensure that form_action() returns a list or tuple of exactly those two things.
It is not clear to me from the post you linked to, or your code, what the action returned is supposed to be - maybe just a result of the action performed. You need to check that to decide whether your action method needs to return something else.
Something like this should work:
def form_action(self, request, admin_approver ):
justification = self.cleaned_data['justification']
action = request.admin_deny(admin_approver=admin_approver, justification=justification)
# action will be None if admin_deny does not return anything,
# but the code calling this function expects it, so return it anyway.
return (request, action)

Django ValidationError

According to https://docs.djangoproject.com/en/dev/ref/forms/validation/
# Good
ValidationError(
_('Invalid value: %(value)s'),
params={'value': '42'},
)
# Bad
ValidationError(_('Invalid value: %s') % value)
The docs doesnt really explain why it is bad / good. Can someone give a concrete example?
Furthermore, when I inspect form.errors, I get something like 'Invalid: %(value)s'. How do I get the params from the Validation error and interpolate them into the error msg?
Edited
So is this considered good?
ValidationError(
_('Invalid value: %(value)s') % {'value': '42'},
)
I think the real question is: why pass the variables separately via the params argument? Why not interpolate directly into the error msg (ignore named or positional interpolation for now)???
Edited
Ok, From the source # https://github.com/django/django/blob/stable/1.5.x/django/forms/forms.py
I don't think there is any way to retrieve ValidationError's params since the Form does not even save the ValidationError object itself. See code below.
class ValidationError(Exception):
"""An error while validating data."""
def __init__(self, message, code=None, params=None):
import operator
from django.utils.encoding import force_text
"""
ValidationError can be passed any object that can be printed (usually
a string), a list of objects or a dictionary.
"""
if isinstance(message, dict):
self.message_dict = message
# Reduce each list of messages into a single list.
message = reduce(operator.add, message.values())
if isinstance(message, list):
self.messages = [force_text(msg) for msg in message]
else:
self.code = code
self.params = params
message = force_text(message)
self.messages = [message]
class Form:
....
def _clean_fields(...):
....
except ValidationError as e:
self._errors[name] = self.error_class(e.messages) # Save messages ONLY
if name in self.cleaned_data:
del self.cleaned_data[name]
If you have multiple parameters, they might appear in a different order when you translate the error message.
Named arguments allow you to change the order in which the arguments appear, without changing params. With a tuple of arguments, the order is fixed.
Note that you are linking to the development version of the Django docs. The validation error is not interpolating the parameters because you are using Django 1.5 or earlier. If you try your code in the 1.6 beta, then the parameters are interpolated into the error message.
ValidationError is caught by the form validation routine and though it can just show a message, it's better to save the possibility of getting params of error; eg. field name, value that caused error and so on. It's stated just before the example you've provided.
In order to make error messages flexible and easy to override

django form: Passing parameter from view.py to forms gives out error

Newbie question:
I need to accept a parameter in a form from a method in views.py but it gave me troubles. In the view I created a method with following snippet:
def scan_page(request):
myClient = request.user.get_profile().client
form = WirelessScanForm(client = myClient) # pass parameter to the form
and in the forms.py I defined the following form:
class WirelessScanForm(forms.ModelForm):
time = forms.DateTimeField(label="Schedule Time", widget=AdminSplitDateTime())
def __init__(self,*args,**kwargs):
myClient = kwargs.pop("client") # client is the parameter passed from views.py
super(WirelessScanForm, self).__init__(*args,**kwargs)
prob = forms.ChoiceField(label="Sniffer", choices=[ x.sniffer.plug_ip for x in Sniffer.objects.filter(client = myClient) ])
But django keeps giving me error saying: TemplateSyntaxError: Caught NameError while rendering: name 'myClient' is not defined(This error happens in the query)
I'm afraid it would be something stupid missing here, but I cannot really figure out why. Please help, thanks.
Assuming I've corrected your formatting properly, you have an indentation issue: prob is outside __init__, so doesn't have access to the local myClient variable.
However if you bring it inside the method, it still won't work, as there are two other issues: first, simply assigning a field to a variable won't set it on the form; and second, the choices attribute needs a list of 2-tuples, not just a flat list. What you need is this:
def __init__(self,*args,**kwargs):
myClient = kwargs.pop("client") # client is the parameter passed from views.py
super(WirelessScanForm, self).__init__(*args,**kwargs)
self.fields['prob'] = forms.ChoiceField(label="Sniffer", choices=[(x.plug_ip, x.MY_DESCRIPTIVE_FIELD) for x in Sniffer.objects.filter(client = myClient)])
Obviously replace MY_DESCRIPTIVE_FIELD with the actual field you want displayed in the choices.

django - how can I clean variable data passed by an url?

When I'm using a form I clean field data using Django forms but how do you clean variable data that's passed by an URL?
For example I have an URL like this: http://mywebsite.com/tags/my-tag/ where my-tag is the variable that I'm passing to a function on my views.py.
I tried to use a Django form to clean the data but I'm getting en error saying "'TagForm' object has no attribute 'cleaned_data'".
I know my-form variable is reaching the tags function in the views.py since I'm able to show its content on a template so the problem is probably with the way I'm using the form.
views.py
def tags(request, my-tag):
tagform = TagForm(request.GET)
cleaned_dt = tagform.cleaned_data
form_tag = cleaned_dt['tag']
forms.py
class TagForm(forms.Form):
tag = forms.CharField()
Any ideas?
The cleaned_data dictionary attribute appears after you call is_valid method on your form.
def tags(request, my-tag):
tagform = TagForm(request.GET)
if tagform.is_valid():
cleaned_dt = tagform.cleaned_data
form_tag = cleaned_dt['tag']
return render(request, "may_template.html", {"form":tagform})
You are creating a TagForm with a request object, but you're not giving the TagForm the value of my-tag anywhere that I can see.
The /my-tag/ section of the URL isn't a request parameter. It's part of the url, and presumably passed to the view function as my-tag (you might want to rename it my_tag to be more Pythonic).
Edit
You can simple create a dict object to initialize to Form object instead of request.GET. An example is here.
data = {'tag': my_tag,
'anotherIfNecessary': 'Hi there'}
tagform = TagForm(data)
Basically, the dictionary used to populate a form object must contain a mapping of form field names to the value you wish to set it at.
In this case, you have a form field name of "tag" and want to set it to my-tag (are you sure you don't get a syntax error with the dash in the variable name? I do...). I've corrected my example.

Having trouble with the manager functionality in django

Designing code to handle transfers of objects called "contractBundles". Have this in the models.py in a "contract" app:
class contractBundleManager():
# def trade(self, buyer, seller, amount):
def add (self, user=pgamedb_models.Player, contractName=str, amount=float):
contractList = contract.objects.all()
for c in contractList:
if contractName == c.contractText:
user.money = user.money - (amount * c.value)
return self.create(contract=c, volume=amount, owner=user)
class contractBundle(models.Model):
objects = contractBundleManager()
contract = models.ForeignKey('contract')
volume = models.FloatField()
owner = models.ForeignKey(pgamedb_models.Player)
def __str__(self):
return '%10.2f x %s -> %s' % (self.volume, self.contract.shortName, self.owner.user.username)
Elsewhere, I want to let people purchase the bundles by pressing a button:
from contracts import models as cmodels
...
if request.method == 'POST':
...
elif 'Buy' in request.POST:
[set up activeUser, polName, amount]
cmodels.contractBundle.objects.add(user=activeUser, contractName=polName, amount=amount)
Yet when the code calls the contractBundle.objects.add() method, I get the following error:
AttributeError at [url]
'Manager' object has no attribute 'add'
Request Method: POST
Request URL: [url]
Django Version: 1.3.1
Exception Type: AttributeError
Exception Value:
'Manager' object has no attribute 'add'
Exception Location: ...views.py in [method], line 56
Python Executable: /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
Python Version: 2.7.1
Any thoughts why this might be happening?
There are a couple of things here that are wrong.
Firstly, your manager class needs to inherit from models.Manager:
class contractBundleManager(models.Manager):
This is where all the code like create is defined.
Also, it seems like a really bad idea to use classes/types as default values for function parameters. If you want to document what types a function needs, you should use a docstring.
Edit after comment No, I'm not referring to the return statement or the way you're calling self.create. I'm referring to the default values in the contractBundleManager.add method. You should never do this. If someone calls your method without passing the user parameter, the function will use the default value you have defined - which is a class. Classes in Python are mutable, so your function will actually modify the definition of the Player class.
Really, this is not the way to show the correct types to call the function with. Leave out the default values, and use a docstring.
#model
class contractBundle(models.Model):
objects = models.Manager( )
contact_bundle_manager = contractBundleManager( )
#view
cmodels.contact_bundle_manager.add(user=activeUser, contractName=polName, amount=amount)