MultipleHiddenInput renders only if with initial data - python

class MyForm(forms.Form):
my_hidden_field = forms.MultipleChoiceField(widget=forms.MultipleHiddenInput, choices=(...))
def my_view(request):
form = MyForm(initial={'my_hidden_field': MyModel.objects.values_list('id', flat=True)})
With such code, if remove initial argument from call to MyForm, my_hidden_field will not be rendered in HTML, but if remove MultipleHiddenInput widget then, it will appear again.
How to make it being rendered properly?

This is just the way it is implemented. If you do not pass any values, then there is nothing to render, because it is hidden anyway, so user just cant alter the values.
You can see the implementation here:
https://github.com/django/django/blob/master/django/forms/widgets.py#L316
If you need to render it, you will need to pass some initial data to it, but it is strange, what is the use case exactly ?

Related

Django using a variable in multiple views/templates

I have a table with results from a search. This means there are no models here. The problem is that i'd like to go to a detail page for a clicked item, but I am not sure if I can do that without putting it in the URL.
Right now it is done like this:
In my .html for each item in the table:
view more</td>
In my urls.py
url(r'^track/(?P<title>.+?)/$', detail,
name='detail'),
In my detail view, where it uses the variable:
def detail(request, title):
if request.method == 'GET':
...
Now this might work, but it is not ideal for me. The url contains whitespace and is not urlencoded, because I need the variable like it is. I was wondering if there is some easier, or better way to pass this variable to a different view or template
The right way to do this is to define a get_absolute_url method on the model class for your item model. This is typically done using reverse, so it would look something like this:
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('detail', kwargs={'title': self.title})
Then in your template you simply do:
view more
And the title will be properly escaped.
That said, you might want to consider using a slug instead of a title to generate your URLs.

Create multiple forms on the same page

I am trying to figure out how to create many forms that are similar on one page. The idea is to have people comment on various sections of a page with text. This means that,
each section has its comments. The forms are the same with a comment field and a submit button.
This is the code i got so far. However it does not work and i definitely need to understand how this can be achieved in a simple way. Can somebody help me figure this out.
In model:
db.define_table('manyforms',
Field('comment'))
In controller:
allforms=dict(form_id=new_form_id(),form=SQLFORM(db.manyforms))
for f in allforms:
if f['form'].accepts(request.vars,formname=f['form_id']):
response.flash = 'accepted'
else:
response.flash = 'refused'
return dict(allforms=allforms)
First of all, this is a useful reference: Multiple forms per page (web2py documentation)
Also, in your function, you are iterating through a dict of two items of different types. You are trying to treat each of those objects (one of which will presumably be an integer, the other, a form) as some object that is indexed by a string key (such as a dict).
Finally, it is not clear from your post what you want your function to do. Does your function correspond to a view? If so, do you really want a bunch of forms that are accessed in your view by a single variable {{=allforms}}? Perhaps from an engineering point of view, a better route is to first create a function that generates a list of forms; maybe something like this:
def generate_forms():
form_id_list = # create id list according to whatever criteria
forms = [SQLFORM(db.manyforms, _id = _id) for _id in form_id_list]
for form in forms:
if form.process(request.vars, formname = form['_id']).accepted:
response.flash = 'accepted'
else:
response.flash = 'refused'
return forms

Easy way to switch between renderers within the same view method

I set up my function like this
#view_config(
route_name = 'route_name',
permissions = 'permissions',
renderer = 'r.mako'
)
def r( request ):
# stuff goes here
now, I want to add functionality such that I check certain conditions (using ajax) i would use one template, otherwise use another. is there a way to do this in pyramid? thanks
Well you can add the view multiple times with different renderers if you can determine what you want to do via predicates. For example
#view_config(route_name='route', xhr=True, renderer='json')
#view_config(route_name='route', renderer='r.mako')
#view_config(route_name='route', request_param='fmt=json', renderer='json')
def r(request):
# ...
Or you can just override the renderer manually via request.override_renderer = 'b.mako':
http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/renderers.html#overriding-a-renderer-at-runtime
Or you can just explicitly render the response via the render and render_to_response methods from within the view, as the renderer argument is ignored if you return a Response object from the view.
Note that the xhr predicate in the first example should be sufficient to check for an ajax request. Note also that you don't have to use the same view for both if you don't want to, just depends.

Clearing Django form fields on form validation error?

I have a Django form that allows a user to change their password. I find it confusing on form error for the fields to have the *'ed out data still in them.
I've tried several methods for removing form.data, but I keep getting a This QueryDict instance is immutable exception message.
Is there a proper way to clear individual form fields or the entire form data set from clean()?
Regarding the immutable QueryDict error, your problem is almost certainly that you have created your form instance like this:
form = MyForm(request.POST)
This means that form.data is the actual QueryDict created from the POST vars. Since the request itself is immutable, you get an error when you try to change anything in it. In this case, saying
form.data['field'] = None
is exactly the same thing as
request.POST['field'] = None
To get yourself a form that you can modify, you want to construct it like this:
form = MyForm(request.POST.copy())
Someone showed me how to do this. This method is working for me:
post_vars = {}
post_vars.update(request.POST)
form = MyForm(post_vars, auto_id='my-form-%s')
form.data['fieldname'] = ''
form.data['fieldname2'] = ''
If you need extra validation using more than one field from a form, override the .clean() method. But if it's just for one field, you can create a clean_field_name() method.
http://docs.djangoproject.com/en/1.1/ref/forms/validation/#ref-forms-validation
I just created the form again.
Just try:
form = AwesomeForm()
and then render it.
Django has a widget that does that:
https://docs.djangoproject.com/en/1.8/ref/forms/widgets/#passwordinput
now if you are not working with passwords you can do something like this:
class NonceInput(Input):
"""
Hidden Input that resets its value after invalid data is entered
"""
input_type = 'hidden'
is_hidden = True
def render(self, name, value, attrs=None):
return super(NonceInput, self).render(name, None, attrs)
Of course you can make any django widget forget its value just by overriding its render method (instead of value I passed None in super call.)
Can't you just delete the password data from the form's cleaned_data during validation?
See the Django docs for custom validation (especially the second block of code).
I guess you need to use JavaScript to hide or remove the text from the container.

Keeping filters in Django Admin

What I would like to achive is:
I go to admin site, apply some filters to the list of objects
I click and object edit, edit, edit, hit 'Save'
Site takes me to the list of objects... unfiltered. I'd like to have the filter from step 1 remembered and applied.
Is there an easy way to do it?
Unfortunately there's no easy way to do this. The filtering does not seem to be saved in any session variable.
Clicking back twice is the normal method, but it can be unweildy and annoying if you've just changed an object so that it should no longer be shown using your filter.
If it's just a one-off, click back twice or go through the filtering again, it's the easiest way.
If you're going to be filtering more often, or you just want to learn about hacking the admin (which is pretty open and easy), you'll want to write a FilterSpec.
Have a look here and here for examples of people writing their own.
A really, really terrible way to do this would be to edit the admin interface so that after you click "Save", you are redirected to you filtered URL. I wouldn't recommend this at all, but it's an option.
Another fairly simple way to do this would be to write a generic view to show your filtered objects, then use Django forms to edit the items from there. I'd have a look at this, you'll be stunned just how little code you have to write to get a simple view/edit page going.
Click 2 times "Back"?
There's a simple hack to do this, but it's not a general solution and requires modifying every ModelAdmin which you want to support this. Maybe there is a general way to do this, but I've not spent the time to solve it on a general level.
The first step is to write a custom FilterSpec for the filter (see Harley's post for links that will help) which saves the chosen filter value in the session (and deletes it when no longer wanted).
# in cust_admin/filterspecs.py
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
class MyFilterSpec(ChoicesFilterSpec):
def __init__(self, f, request, params, model, model_admin):
super(MyFilterSpec, self).__init__(f, request, params, model,
model_admin)
if self.lookup_val is not None:
request.session[self.lookup_kwarg] = self.lookup_val
elif self.lookup_kwarg in request.session:
del(request.session[self.lookup_kwarg])
# Register the filter with a test function which will apply it to any field
# with a my_filter attribute equal to True
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'my_filter', False),
MyFilterSpec))
You must import the module this is in somewhere, for example your urls.py:
# in urls.py
from cust_admin import filterspecs
Set a property on the field you want to apply the filter to:
# in models.py
class MyModel(models.Model):
my_field = Models.IntegerField(choices=MY_CHOICES)
my_field.my_filter = True
In a custom ModelAdmin class, override the change_view method, so that after the user clicks save, they are returned to the list view with their filter field value added to the URL.
class MyModelAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, extra_context=None):
result = super(MyModelAdmin, self).change_view(request, object_id,
extra_context)
if '_save' in request.POST:
if 'my_field__exact' in request.session:
result['Location'] = '/admin/myapp/mymodel/?my_field__exact=%s' \
% request.session['my_field__exact']
return result
Another way to do this is to embed the filter in the queryset.
You can dynamically create a proxy model with a manager that filters the way you want, then call admin.site.register() to create a new model admin. All the links would then be relative to this view.
In my opinion its better to override methods from ModelAdmin changelist_view and change_view:
Like so:
class FakturaAdmin(admin.ModelAdmin):
[...]
def changelist_view(self, request, extra_context=None):
result = super(FakturaAdmin, self).changelist_view(request, extra_context=None)
request.session['qdict'] = request.GET
return result
def change_view(self, request, object_id, extra_context=None):
result = super(FakturaAdmin, self).change_view(request, object_id, extra_context)
try:
result['location'] = result['location']+"?"+request.session['qdict'].urlencode()
except:
pass
return result
As you wish, after save object you go back to list of objects with active filters.
There is a change request at the Django project asking for exactly this functionality.
All it's waiting for to be checked in is some tests and documentation. You could write those, and help the whole project, or you could just take the proposed patch (near the bottom of the page) and try it out.
https://code.djangoproject.com/ticket/6903
This feature has been added to Django as part of the 1.6 release and is enabled now by default. It is described in the release notes:
ModelAdmin now preserves filters on the list view after creating,
editing or deleting an object. It’s possible to restore the previous
behavior of clearing filters by setting the preserve_filters attribute
to False.

Categories

Resources