django forms.ChoiceField random choices - django

in the Forms I add a field with the parameter
login = forms.ChoiceField(choices=gen_login())
When using gen_login(), a list is generated and the values do not change randomly during the subsequent page refresh.
If you use gen_login, the list is updated when the page is refreshed, but it does not pass validation
def gen_login():
return map(lambda x: (x, x), [(''.join(random.choices(string.ascii_uppercase, k=2)) + '_') for i in range(6)])

Related

How can I return a new non-repeating item from a list in Django while keeping track of previous items?

I'm working on an app where a user can select a category, which will return a random selection from that category. The main functionality I'm trying to implement is once an item is selected, it can no longer be randomly selected in session.
For example, we have 3 categories of photos: landscape, urban, and portraits, each with 5 photos. A user selects urban, is then redirected to a details page with a random photo from urban category. He can either refresh the page or click a button to get a new photo from that category. When that category is out of new photos, he is redirected home.
I am able to get my random item from the selected category through converting a queryset to a list, but the data isn't persisting. On every refresh the list I have resets, thus a previously selected photo can come up again, ignoring the the fact that I removed the item from the list after it was selected.
Here's the views.py with the function responsible for this:
def randomPhoto(request, pk, **kwargs):
# queryset to get all photos from selected category
gallery = list(Photos.objects.filter(id=pk)
.values_list("partof__category", flat=True))
# select random photo from list
last = len(gallery) -1
randomInt = random.randint(0, last)
randomPic = gallery[randomInt]
gallery.remove(randomPic)
if len(gallery) == 0:
return render(request, 'gallery/category_select.html')
photoDetails = {
'category' : Category.objects.get(id=pk),
'author' : Author.objects.get(tookin__category=randomPic),
'uploadedPhoto' : 'http://localhost:8000/media/' +
str(Photo.objects.get(category=randomPic).photoUpload),
'randomPic' : randomPic,
}
return render(request, 'gallery/random_photo.html', {'photoDetails': photoDetails})
The functionality I'm looking for is (where each number is an object/item in list):
User selects urban category:
urban has the following items: [1, 2, 3, 4, 5]
random [3] selected from urban
urban now has [1, 2, 4, 5]
User refreshes:
random [4] selected
urban now has [1, 2, 5]
User refreshes:
random [2] selected
urban now has [1, 5]
User refreshes:
random [5] selected
urban now has [1]
User refreshes:
random [1] selected
urban now has []
User is redirected home
I believe my problem lies in having to configure either sessions or cookies to have the data persist in an anonymous session. Eventually I will be adding a Users module so each user will have their browsing history saved but for now I want it to work just as an anonymous user.
I've tried adding SESSION_SAVE_EVERY_REQUEST = True to settings.py and placing request.session.modified = True in my views.py, though I doubt I'm implementing them properly. I've read some SO questions on sessions and cookies but wasn't able to find something to work with my issue. The Django Sessions Doc seemed interesting but overwhelming. I'm not sure where to begin trying to experiment with wiring the sessions aspect together.
I am wondering if there's an easy/Pythonic way to achieve having my web app give me a non-repeating item from a list until none are left within the session.
Your issue is that your variable is not carried over from one request to the next. The best way to do this would be to use request.session = ... to set a variable, and then check it later and perform actions. Here is an example that you can expand on to make it to your liking:
import random
from django.shortcuts import redirect
class TestView(View):
def get(self, request, *args, **kwargs):
gallery = request.session.get('gallery', None)
if (type(gallery) is list) and (len(gallery) == 0): # When list is empty, clear session & then redirect
del request.session['gallery']
request.session.modified = True
return redirect('<your_redirect_url>')
if gallery is None: # If first visit to page, create gallery list
gallery = list(models.Photos.objects.all().values_list("partof__category", flat=True))
# select random photo from list
last = len(gallery) -1
randomInt = random.randint(0, last)
randomPic = gallery[randomInt]
gallery.remove(randomPic)
request.session['gallery'] = gallery
return render(request, 'test.html', {})

how to get initial data on the extra forms

i've got multiple forms, which i want to pull the same initial data into specific fields
but only the first formset is doing the job but the rest come blank
also note that CustomerForm is a modelform
CustomerFormset = formset_factory(CustomerForm, extra=2)
form = CustomerFormset(initial=[{'Location':'USA', 'age': 10} ])
context = { 'form':form }
return render(request,"Customermanager/SubProcess.html",context)
so when the form loads... all the rest of the extra COME BLANK!
i want both my intial plus extra to have the same data
if i can get some code how to do it. ill beable to fill in the rest
In order to set different initial values for each form in a formset including the extra forms, you have to pass a list of dictionaries containing the initial data for each form to be rendered.
Something like the following would do :
Formset = formset_factory(CustomerForm, extra=2)
formset = FormSet(initial=[{'Location':'USA', 'age': 10} for x in range(3)])
You might also be interested in This.

Django (1.9) paginate different items on first page

I have a different design on the first page of a list, and need 8 items on the first page. On the rest I would like 9 items. I have tried to specify items on the first page, but the second page thinks the 9.th item was shown on the first page, it was not. Anyone know if it is possible to fix this?
class EntryList(ListView):
model = Entry
def get_paginate_by(self, queryset):
page_kwarg = self.page_kwarg
page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or '1'
if page == '1':
self.paginate_by = 8
else:
self.paginate_by = 9 #This assumes the 9.th was shown on the first page
return self.paginate_by

Need clarification on using Django 1.4 Form Wizards, specifically pre-filling and saving

We are building a wizard using Django 1.4's new form wizard functionality.
The docs on this are very terse and we can't find any advanced examples. We are using a named step wizard (needed to support a listview/datagrid we use) and a session backend.
The wizard is meant to edit roles and linked rights and is built to provide both add and edit functionality. We do this by asking the user in the first step if he/she wants to add or edit.
The next step depends on that choice;
If the user wants to edit, there is a search screen, followed by a listview/datagrid that displays results. The user can then select one of the results and goes to a details-screen, followed by a FilteredSelectMultiple page, allowing him/her to link rights to this role.
If the user wants to add a new role, the search and results screens are skipped and the user goes directly to the details screen, followed by the link-screen.
It all works pretty well, using a condition_dict in urls.py, but we are wondering a couple of things about the general functionality:
When a specific pre-existing role is selected, how can we fill the details and the link-screen with the corresponding data?
Do we instantiate a roles-object and pass it somehow to the two forms, if so, where do we instantiate it and do we need to do that for every form separately (which seems a bit over the top)?
When saving, is it common practice to create another instance of a role object, add the form data to it and save, or can we re-use the object used in the forms somehow?
We have tried overloading get_form_instance to return instances of roles, and we have looked at instance_dict in the docs, but it feels like the wrong approach and there are no examples to be found online, and we're not even sure these are used to pre-fill data or even if we're on the right track.
Logically, I would say in the step that selects an existing role, I need to fill the wizard-variables using an instance of the chosen object, and these get displayed in the forms. At the end of the wizard we reverse the process and get all data from the wizard-variables and add them to a newly instantiated roles-object and save it. Ideally this instance will determine itself if it needs to perform an INSERT or an UPDATE, depending on whether or not the promary key is filled.
If anyone can provide an example, or a nudge in the right direction, it would be very much appreciated.
The code of the wizardview class in views.py is below:
class RolesWizard(NamedUrlSessionWizardView):
def get_template_names(self):
# get template for each step...
if self.steps.current == 'choice':
return 'clubassistant/wizard_neworeditrole.html'
if self.steps.current == 'search':
return 'clubassistant/wizard_searchrole.html'
if self.steps.current == 'results':
return 'clubassistant/wizard_pickrole.html'
if self.steps.current == 'details':
return 'clubassistant/wizard_detailsrole.html'
elif self.steps.current == 'rights':
return 'clubassistant/wizard_roles.html'
def get_context_data(self, form, **kwargs):
# get context data to be passed to the respective templates
context = super(RolesWizard, self).get_context_data(form=form, **kwargs)
# add the listview in the results screen
if self.steps.current == 'results':
# get search text from previous step
cleaned_data = self.get_cleaned_data_for_step('search')
table = RolesTable(Roles.objects.filter(
role_name__contains=cleaned_data['searchrole'])
)
RequestConfig(self.request, paginate={
"per_page": 4,
}).configure(table)
# add the listview with results
context.update({'table': table})
# add a role instance based on the chosen primary key
if self.steps.current == 'rights':
cleaned_data = self.get_cleaned_data_for_step('results')
role_id = cleaned_data['role_uuid']
role = get_object_or_404(Roles, pk=role_id)
context.update({'role': role})
return context
def done(self, form_list, **kwargs):
# this code is executed when the wizard needs to be completed
# combine all forms into a single dictionary
wizard = self.get_all_cleaned_data()
if wizard.get("neworeditrole")=="add":
role = Roles()
else:
role = get_object_or_404(Roles, pk=wizard.get("role_uuid"))
# many-to-many rights/roles
role.role_rights_new_style.clear()
for each_right in wizard.get('role_rights_new_style'):
RightsRoles.objects.create(role=role, right=each_right,)
# other properties
for field, value in self.get_cleaned_data_for_step('details'):
setattr(role, field, value)
role.save()
# return to first page of wizard...
return HttpResponseRedirect('/login/maintenance/roles/wizard/choice/')
For future googlers:
I had some success with using get_form() because it is called before a form is rendered. Start with a couple of ModelForms:
class Wizard1(models.ModelForm):
class Meta:
model = MyModel
fields = ('field0', 'model0')
class Wizard2(models.ModelForm):
class Meta:
model = MyModel
excludes = ('field0', 'model0')
Then, in your SessionWizardView:
class MyWizard(SessionWizardView):
def get_form(self, step=None, data=None, files=None):
form = super(ExtensionCreationWizard, self).get_form(step, data, files)
if step is not None and data is not None:
# get_form is called for validation by get_cleaned_data_for_step()
return form
if step == "0":
# you can set initial values or tweak fields here
elif step == "1":
data = self.get_cleaned_data_for_step('0')
if data is not None:
form.fields['field1'].initial = data.get('field0')
form.fields['field2'].widget.attrs['readonly'] = True
form.fields['field3'].widget.attrs['disabled'] = True
form.fields['model1'].queryset = Model1.objects.filter(name="foo")
return form
The action is all in step 1. You request validated data from step 0 (which triggers another call to get_form() for step 0, so be careful) and then you can access any values that were set in step 0.
I threw in a couple of examples of settings you can change on the fields. You can update a queryset to limit the values in a ChoiceField, or re-display a value again but make it read-only. One caveat I noticed... readonly does not work on ChoiceField. You can make it disabled, but then the value is not propagated when you submit the form.
Let's see if I can help. I did a form wizard that adds steps depending on the answers. At each step I save all forms in a session variable, like so:
def process_step(self, request, form, step):
request.session['form_list'] = self.form_list
request.session['initial'] = self.initial
Then, each time that view is rendered, I instantiate a new form wizard with all the previous data:
def dynamic_wizard(request):
if not request.session.get('form_list'):
form = Wizard([Form1])
else:
form = Wizard(request.session.get('form_list'), initial = request.session['initial'])
return form(context=RequestContext(request), request=request)

Flexible pagination in Django

I'd like to implement pagination such that I can allow the user to choose the number of records per page such as 10, 25, 50 etc. How should I go about this? Is there an app I can add onto my project to do this?
Thanks
Django has a Paginator object built into core. It's a rather straightforward API to use. Instantiate a Paginator class with two arguments: the list and the number of entries per "page". I'll paste some sample code at the bottom.
In your case you want to allow the user to choose the per-page count. You could either make the per-page count part of the URL (ie. your/page/10/) or you could make it a query string (ie. your/page/?p=10).
Something like...
# Assuming you're reading the Query String value ?p=
try:
per_page = int(request.REQUEST['p'])
except:
per_page = 25 # default value
paginator = Paginator(objects, per_page)
Here's some sample code from the Django doc page for the Paginator to better see how it works.
>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)
>>> p.count
4
>>> p.num_pages
2
>>> p.page_range
[1, 2]
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']
google on "django pagination" and make sure to use "covering index" in your SQL for efficient query.
T. Stone's answer covers most of what I was going to say. I just want to add that you can use pagination in Generic Views. In particular, you may find django.views.generic.list_detail.object_list useful.
You can write a small wrapper function that gets the number of objects to display per page from the request object, then calls object_list.
def paginated_object_list(request, page):
my_queryset=MyModel.objects.all()
#Here's T. Stone's code to get the number of items per page
try:
per_page = int(request.REQUEST['p'])
except:
per_page = 25 # default value
return object_list(request, queryset=my_queryset,
paginate_by=per_page, page=page)
Then, the context for your template will contain the variables,
paginator: An instance of django.core.paginator.Paginator.
page_obj: An instance of django.core.paginator.Page.
and you can loop through page_obj to display the objects for that page.
What does need? Well.
You can add custom control for change_list.html, for pagination block for example.
This will be reload list page with get parameter per_page for example with certain value onchange event.
For your adminModel you must override changelist_view method where you must handle get parameter and set this value as list_per_page field value.
def changelist_view(self, request):
if request.GET.get('per_page') and int(
request.GET.get('per_page')) in CHANGELIST_PERPAGE_LIMITS:
self.list_per_page = int(request.GET.get('per_page'))
else:
self.list_per_page = 100
extra_context = {'changelist_perpage_limits': CHANGELIST_PERPAGE_LIMITS,
'list_per_page': self.list_per_page}
return super(mymodelAdmin, self).changelist_view(request, extra_context)
I use extra_context for access to this values into template. Maybe there is more neat approach to access i don't know :-)