Django FormWizard Dynamically Alter form_list - django

I'm having some issues with the form wizard, that maybe someone can shed some light on. According docstring in the method process_step: I can "dynamically alter self.form_list". So, based on my project needs, I'm appending forms to the form_list. The forms I'm appending contain questions and answers: http://dpaste.com/hold/152201/
The issue is that when 2 people hit the formwizard at the same time, they start to see each others questions and answers. I guess I don't understand how process_step suggests I can dynamically alter the form_list, when by doing so I'm modifying the form list of another user.
Is the form_list a shared object among visitors hitting the formwizard url defined in urls.py? I've seen this issue under apache2/prefork/worker/mod_wsgi, and while running the app with runserver.

How do you use FormWizard? If you're putting it in urls.py like docs says then it could be cached, i had that issue couple of times. Just put it in a view like:
def my_view(request):
return FormWizard(request)
UPDATE: Example from real
def registration_wizard(request, template_name=None):
rw = RegistrationWizard([RegistrationForm, 0])
#hack formwizard to replace default template
if template_name:
rw.get_template = lambda x: template_name
return rw(request)
here RegistrationWizard is a FormWizard subclass with dynamic form_list, [RegistrationForm, 0] is needed because if there's only one form at creation time, wizard won't get to form_list function. Template thing is pretty self-explanatory

Related

Dynamic get_absolute_url using url query parameters

Big picture: I'd like my reverse method in get_absolute_url (see below) to return a url with a query parameter appended to it at the end, e.g. <url>?foo=bar. Further, I'd like bar to be specified by the POST request that triggered the call to get_absolute_url, either as an input to the form (but not a field represented by the model, something temporary) or as a url query parameter. I am easily able to access bar in my view using either method, but I can't seem to figure out how to access it in my model.
The motivation here is that my detail page splits up the fields from my model into different tabs using javascript (think https://www.w3schools.com/howto/howto_js_tabs.asp). When the user is updating the model, they choose which tab they want to update, and then the update template only renders the fields from the model which are related to that tab. More importantly, after the user submits the update, I want the detail page to know to open the specific tab that the user just edited.
(I understand how this works if the field is a part of the model; in get_absolute_url with parameters, the solution is pretty straightforward and involves using self.id. In my case though, bar is not a part of the model and I can't figure out how else to access it)
Some specifics: I have a model in my project called Context. I have implemented a generic DetailView and an update page for the model using a modelform called ContextForm and a generic UpdateView called ContextUpdate. Once the form is submitted, I redirect to the detail page using get_absolute_url in models.py:
def get_absolute_url(self):
return reverse("context:review",kwargs={"slug": self.slug})
My urlpatterns in urls.py looks something like:
urlpatterns = [
url(r'^(?P<slug>[-\w]+)$',views.ContextDetail.as_view(),name="review"),
url(r'^(?P<slug>[\w]+)/edit$',views.ContextUpdate.as_view(),name="edit"),
]
I am able to access this parameter in my UpdateView quite easily:
def post(self,request,**kwargs):
print (request.POST.get("bar")) #accessing input to form
print (request.GET.get("bar")) #accesssing url parameter
return super().post(request,**kwargs)
But when get_absolute_url is called inside the model, it seems I no longer have access to it.
Any suggestions for how to accomplish this? I want to use get_absolute_url (along with modelforms, generic views, etc.) so that I can follow Django conventions, but it seems like using get_absolute_url is making the functionality that I want difficult to accomplish. If the redirect to the detail view following the POST request were to happen inside my view, then I would know how to solve this (I think). Any thoughts or ideas would be greatly appreciated!
As you say, you can't access the request inside your get_absolute_url method. Therefore you should override get_success_url, from which you can access it.
def get_success_url(self):
return reverse(reverse("context:review", kwargs={"slug": self.object.slug}) + '?bar=%s' % self.request.GET.get('bar')
Or if you want to re-use get_absolute_url:
def get_success_url(self):
return self.object.get_absolute_url + '?bar=%s' % self.request.GET.get('bar')
The second option is DRYer but would break if get_absolute_url was changed to include a querystring like ?foo=foo.

Django Rest Framework pre-populate form with specific queryset

Basically in a popup (bootstrap) I would like to have all specified pre-populated fields from my model.
I found this code (https://groups.google.com/forum/#!searchin/django-rest-framework/HTMLFormRenderer/django-rest-framework/s24WFvnWMxw/hhmaD6Qw0AMJ)
class CreatePerformanceForm(forms.ModelForm):
model = Performance
fields = ('field1', 'field2')
class PerformanceCreateView(ListCreateAPIView):
serializer_class = PerformanceCreateSerializer
model = Performance
template_name = 'core/perform.html'
def get(self, request, format=None):
data = {'
form': CreatePerformanceForm()
}
return Response(data)
My question is the same.
Is there a way to create the form directly from the serializer so I don't have to create a Django form?
I looked at HTMLFormRenderer, but the DRF doc is quiet poor about this issue.
Thanks,
D
See this issue. Important part:
There are some improvements that could be made there [to HTMLFormRenderer], notably supporting error messaging against fields, and rendering the serializer directly into html without creating a Django form in order to do so [...]
So basically, HTMLFormRenderer also uses Django forms. Also, you are right, the documentation doesn't provide too much support for it. Even more, it seems that this renderer might soon change. See here. Quote:
Note that the template used by the HTMLFormRenderer class, and the context submitted to it may be subject to change. If you need to use this renderer class it is advised that you either make a local copy of the class and templates, or follow the release note on REST framework upgrades closely.
I know this doesn't help much, but for now there is no better way than the way you did it.

Make Django Admin remember my parameters after posting

I have a problems thats been bugging me for a while. I want to use dates in django admin to view entries between certain dates.
To do this I have customized my changelist.html for this model and put a form in there. When posted I override the queryset method like this
def queryset(self, request):
qs = super(ModelAdmin, self).queryset(request)
if request.POST.has_key('date1'):
return qs.filter(startdate__gte=request.POST['date1']).filter(startdate__lte=request.POST['date2'])
return qs
This works great but its only one little problem. The parameters are forgotten if I for example choose to sort the result in any way.
If I instead of this type in the url straight into the browser so it looks like this
http//localhost/admin/some/model/?startdate__gte=2010-01-01&startdate__lte=2010-12-30
I can sort however I want to afterwards because they´ll stick just like this
http//localhost/admin/some/model/?o=5&ot=asc&startdate__lte=2010-12-30&startdate__gte=2010-01-01
Do I need to use a filterspec to solve this?
Thanks heaps!
There is a change request over at the Django project asking for this functionality.
It's waiting for someone to write tests for the proposed patch before it's committed, so you could either do that or you could download the proposed patch (near the bottom of the page) and use it.
https://code.djangoproject.com/ticket/6903

Building a formset dynamically

I initially wrote code to build a form dynamically, based on data from the DB, similar to what I described in my previous SO post.
As SO user Daniel Roseman points out, he would use a formset for this, and now I've come to the realization that he must be completely right. :)
My approach works, basically, but I can't seem to get validation across the entire form to be working properly (I believe it's possible, but it's getting quite complex, and there has to be a smarter way of doing it => Formsets!).
So now my question is: How can I build a formset dynamically? Not in an AJAX way, I want each form's label to be populated with an FK value (team) from the DB.
As I have a need for passing parameters to the form, I've used this technique from a previous SO post.
With the former approach, my view code is (form code in previous link):
def render_form(request):
teams = Team.objects.filter(game=game)
form_collection = []
for team in teams:
f = SuggestionForm(request.POST or None, team=team, user=request.user)
form_collection.append(f)
Now I want to do something like:
def render_form(request):
teams = Team.objects.filter(game=game)
from django.utils.functional import curry
from django.forms.formsets import formset_factory
formset = formset_factory(SuggestionForm)
for team in teams:
formset.form.append(staticmethod(curry(SuggestionForm, request.POST or None, team=team, user=request.user)))
But the append bit doesn't work. What's the proper way of doing this?
Thanks!
Thanks for the recognition of my unvarying rightness...
Probably what you need here is a model formset, which will automatically build itself from a queryset you pass in:
from django.forms.models import modelformset_factory
def render_form(request):
teams = Team.objects.filter(game=game)
formset = modelformset_factory(form=SuggestionForm, queryset=teams)
As for the dynamic parameter, which I'm guessing is user, I've previously used the closure solution, but the curry method should still work:
formset.form = staticmethod(curry(SuggestionForm, user=request.user))
Edit after comment Thanks for the clarification. I think I understand what you're trying to do. I wonder if an inline formset might work better? If you started off with a Game object pre-populated with eight related Team objects, an inline formset would give you eight pre-existing forms.
my_game = Game.objects.create(params=whatever)
for i in range(1, 9):
team = Team.objects.create(game=my_game, name="team_%s" % i
formset = inlinemodelformset_factory(Game, Team, form=SuggestionForm)
fs = formset(instance=my_game)
Does that work?

django pagination - Remembering checked checkboxes across pages

I actually want to accomplish the same thing a user in
Remembering checked checkboxes across pages - what's the best way?
asked. But not for php, I want it for django. Since django is just great :-)
I`m using pagination and want to remember the checked checkbox while the user is navigating over the pages provided by pagination.
Now I'm searching for a best practice how I could accomplish this. I do not really have a good idea how i could accomplish this. My idea would include javascript, but I'm sure there is somewhere out there a solution without js.
I can't think of any way to do this with a paginator. But this is a good spot for a formwizard and dynamic forms. The general idea is to create a dynamic form for each "page" and then a formwizard to hold them all together. This allows for saving the checkboxes across multiple pages and lets you go backwards and forwards easily. The best thing is that it takes virtually no code!
Something like this should be able to deal with everything:
from django.contrib.formtools.wizard import FormWizard
from django import forms
from django.forms.extras.widgets import CheckboxSelectMultiple
from django.core.paginator import Paginator
# In your forms.py --------------
def MPageFormMaker(paginator):
"""
Create a "paginated" group of forms based on a queryset and num-per-page item.
"""
def Sform(this_q):
"""
Create this page's items
"""
class _PageForm(forms.Form):
items = forms.ModelMultipleChoiceField(queryset = this_q,
widget = forms.CheckboxSelectMultiple)
return _PageForm
for i in range(paginator.num_pages):
yield Sform(paginator.page(i).object_list)
class MpageForm(FormWizard):
def done(self, request, formlist):
#do something with the list of forms
#----- In your views.py
def MpageChecker(request):
qset = Item.objects.all()
paginator = Paginator(qset, 30)
formwizard = MPageForm(list(MPageFormMaker(paginator)))
#then deal with it like a normal formwizard
Essentially just instantiate the formwizard class and then let it take care of everything. Since it uses a paginator class to make the forms you can use any sort of personalization you'd like.
BTW: I haven't tested this code so it may have a few typos but it should be enough to get you on your way.
EDIT ... fix the ordering problem, now all ordering should be preserved correctly across pages!