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!
Related
I am using Django with crispy_forms third party library. I want to add a link beside a form field like some forms in Django admin app. How can I do this?
You've picked quite a complicated example that uses a method I wouldn't even recommend. But I'll try to explain how you see what you're seeing, and keep it short.
That is a AdminTimeWidget(forms.TimeInput) and a AdminDateWidget both nested in a AdminSplitDateTime(SplitDateTimeWidget(MultiWidget)). The MultiWidget part of this isn't really important, that's just how you bind two widgets together to provide one value (a datetime.datetime).
Here's what AdminTimeWidget looks like:
class AdminTimeWidget(forms.TimeInput):
#property
def media(self):
extra = '' if settings.DEBUG else '.min'
js = [
'vendor/jquery/jquery%s.js' % extra,
'jquery.init.js',
'calendar.js',
'admin/DateTimeShortcuts.js',
]
return forms.Media(js=["admin/js/%s" % path for path in js])
def __init__(self, attrs=None, format=None):
final_attrs = {'class': 'vTimeField', 'size': '8'}
if attrs is not None:
final_attrs.update(attrs)
super().__init__(attrs=final_attrs, format=format)
That adds a DateTimeShortcuts.js script to the page (in the way that Admin Widgets can, via the form media property) and it's that script that iterates input tags looking for date and time inputs.
There's a LOT of machinery involved to get that happening but again, in effect, it's just a bit of javascript that looks for a date/time input and adds the HTML client-side.
But you probably don't want to do that.
As I said, that's a very complicated widget, and in Admin where it's harder to alter things on the fly. If you want to write an Admin widget, you probably do want to go that way.
But if you already control the template, or a crispy layout, you could just bung in some HTML. Crispy has an HTML element that you can throw into layouts. This is well documented.
Or if you want a reusable widget, you could use a custom template. Since Django 1.11, Widgets use templates to render.
Create a widget, borrowing from an existing one to save time
from django.forms import widgets
class DateWithButtonWidget(widgets.DateInput):
template_name = 'widgets/date_with_button.html'
Customise the template with the HTML you want:
{% include "django/forms/widgets/input.html" %} <button>MY BUTTON</button>
Use that widget in your form:
class MyForm(forms.ModelForm):
fancydate = forms.DateField(widget=DateWithButtonWidget)
Of course, wiring that button to do something is all up to you. Using a fully-scripted option might be what you need after all.
(Django 1.8) I have a table which has 4 many-to-many relationship to other tables.
Two of these tables have so many entries and that is causing the admin page to load very slowly because it is trying to load all the entries in the lists.
Is there a way to avoid the internal admin page query for loading all the entries of the big tables to speed up the admin page load?
I think the best way is to only list the selected values but I'm not sure how.
I'm not sure how to use limit_choices_to in here:
class Data(models.Model):
pass # stuff here
class Report(models.Model):
data= models.ManyToManyField(Data)
I also tried adding this to my admin.py but it did not help at all. It's not limiting for some reason:
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
if len(qs) > 10:
qs = qs[:10]
return qs
If you still want to use limit_choices_to, then please refer to the docs. You basically just supply the filters in a dictionary object.
To speed up the admin, my suggestions include:
1. Using raw_id_fields in your ModelAdmin. This gives you a little searchbox instead of a selectbox and avoids the overhead of listing all related objects.
2. If you are handling forward ForeignKey relationships, you can use list_select_related in your ModelAdmin as well. In your case, you are handling many-to-many relationships, so you can try overriding the get_queryset method of the ModelAdmin, and use prefetch_related like in the code below.
from django.contrib import admin
class TestModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
test_model_qs = super(TestModelAdmin, self).get_queryset(request)
test_model_qs = test_model_qs.prefetch_related('many-to-many-field')
return test_model_qs
If you really like to get your hands dirty, I highly recommend you use django-debug-toolbar. It really gives you visibility on how many and what SQL statements are being run. If you can read SQL, you can deduce what you need to input to select_related and prefetch_related.
In the database, I have a set of questions. I want to display every question in a collapsible item as a list. Previously I used TemplateView:
class questionmanager(TemplateView):
template_name = 'questionmanager.html'
questions = Question.objects.all()
def get_context_data(self, **kwargs):
context = ({
'questions': self.questions,
})
return context
Then, I read that using ListView is better practice to represent a list of objects. Then I changed my class to this:
class QuestionListView(ListView):
model = Question
def get_context_data(self, **kwargs):
context = super(QuestionListView, self).get_context_data(**kwargs)
return context
In the old template I used this for loop:
{% for question in questions %}
I thought I wouldn't need to use a for loop when I use ListView instead of TemplateView; but I couldn't list the items without a for loop. I found an example here, and it seems to me, the only difference is that in the for loop we use object_list ( {% for question in **object_list** %}) instead of using argument that we pass in the context.
I really don't see so much difference between using TemplateView and ListView - after spending an hour on this. I'd appreciate if someone explains why using ListView instead of TemplateView is a better practice (in this case).
Thanks in advance.
For simple use cases such as this, there isn't much difference. However, the ListView in this example is much cleaner as it can be reduced to:
class QuestionListView(ListView):
model = Question
considering you aren't putting anything in the context. TemplateView's as a base view are rather rudimentary, and provide a much smaller set of methods and attributes to work with for the more complex use cases, meaning you have to write more code in such instances. If you take a look and compare both views TemplateView and ListView here, you can see the difference more clearly. Pagination is a good example, to paginate a ListView you simply set the paginate_by attribute and modify your template accordingly.
Also note, you can change the default name object_list by setting context_object_name in the 'ListView'
The major difference is that ListView is best to list items from a database called model, while TemplateView is best to render a template with no model. Both views can be very concise with different meaning.
Below is sample of a list view in it simplest form
Class SampleListView(ListView):
model = ModelName
This will give you a context variable object_list and a template name in the form ("app_name/model_name_list.html") automatically that can be used to list all records in the database.
However, the TemplateView in its simplest form is
class SampleTemplateView(TemplateView):
template_name = 'app_name/filename.html'
I read today that Django 1.3 alpha is shipping, and the most touted new feature is the introduction of class-based views.
I've read the relevant documentation, but I find difficult to see the big advantage™ that I could get by using them, so I'm asking here for some help in understanding them.
Let's take an advanced example from the documentation.
urls.py
from books.views import PublisherBookListView
urlpatterns = patterns('',
(r'^books/(\w+)/$', PublisherBookListView.as_view()),
)
views.py
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from books.models import Book, Publisher
class PublisherBookListView(ListView):
context_object_name = "book_list"
template_name = "books/books_by_publisher.html",
def get_queryset(self):
self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
return Book.objects.filter(publisher=self.publisher)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherBookListView, self).get_context_data(**kwargs)
# Add in the publisher
context['publisher'] = self.publisher
return context
And now let's compare it to a “plain-old-views” solution, made by myself in 5 minutes for this question (I apologize for any error you may find in it).
urls.py
urlpatterns = patterns('books.views',
url(r'^books/(\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)
views.py
from django.shortcuts import get_object_or_404
from books.models import Book, Publisher
def publisher_books_list(request, publisher_name):
publisher = get_object_or_404(Publisher, name__iexact=publisher_name)
book_list = Book.objects.filter(publisher=publisher)
return render_to_response('books/books_by_publisher.html', {
"book_list": book_list,
"publisher": publisher,
}, context_instance=RequestContext(request))
The second version to me looks:
Equivalent in functionality
A lot more readable (self.args[0]? awful!)
Shorter
Not less DRY-compliant
Is there something big I'm missing? Why should I use them? Are those on the documentation? If so then what would be the ideal use case? Are mixins that useful?
Thanks in advance to anybody who contributes!
P.S. for those who might wonder, I was never enthralled by generic views as well: as soon as I needed some advanced functionality, they became no shorter than regular views.
You can subclass a class and refine methods like get_context_data for specific cases, and leave the rest as-is. You can't do that with functions.
For instance, you might need to create a new view that does everything a previous one does, but you need to include extra variable in the context. Subclass the original view and override the get_context_data method.
Also, separating the steps needed to render the template into separate methods promotes clearer code - the less done in a method, the easier it is to understand. With regular view functions, it's all dumped into the one processing unit.
If self.args[0] is bothering you, the alternative is:
urlpatterns = patterns('books.views',
url(r'^books/(?P<slug>\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)
Then you could use self.kwargs['slug'] instead, making it slightly more readable.
Your example function and class are not equal in features.
The class based version provide pagination for free and forbid the use of other HTTP verbs than GET.
If you want to add this to your function, it's going to be much longer.
But it is, indeed, more complicated.
This is the first I'm hearing of this -- and I like it.
The advantage I see here, honestly, is that it makes views more consistent with Django overall. Models are classes and I've always felt that views should be too. I know not everything is but views and models are the two heavily used types.
As for the technical advantage? Well, in Python everything is a class (or object?) -- so is there really a difference? Isn't it 99% syntactical sugar in the first place?
One way to think about class based views, is that they are like a the Django admin with training wheels off and therefore a lot more flexible (but more difficult to understand).
For example the list-display in the admin is clearly based on the generic ListView. The simplest list view you would only define a model or queryset.
class MyExampleView(ListView);
model = ExampleModel
You will need to supply your own template, but it will basically be the same as the most basic ModelAdmin. The list_display attribute in the model admin will tell it what fields to display, whereas in the ListView you would do this in the template.
class SpeciesAdmin(admin.ModelAdmin):
list_display = ['name']
admin.site.register(ExampleModel , ExampleModelAdmin)
With the admin you have a parameter
list_per_page = 100
which defines how many objects per page. List view has
paginate_by = 100
which achieves the same thing. Likewise if you look into customising the admin heavily, you will see a lot of overlap.
This site here should give you a better idea of what they do as well.
http://ccbv.co.uk/
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