Make Django Admin remember my parameters after posting - django

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

Related

Prefetch_related on queryset.get()

Today I have written a DRF view method using prefetch_related:
def post(self, request, post_uuid, format=None):
post = Post.objects.prefetch_related('postimage_set').get(uuid=post_uuid)
postimage_set = post.postimage_set.all()
for image in postimage_set:
...
return Response('', status.HTTP_200_OK)
And I fear that I am using prefetch_related wrongfully with this. Does it make sense to use prefetch_related here or will this fetch all posts as well as all postimages and then filter this set to just one instance? I'm super thankful for any help on this.
Looks kinda unnatural. Without looking at your database structure I can only guess, that what you really want to do is:
PostImage.objects.filter(post__uuid=post_uuid) (mind the usage of a dunder between post and uuid - that simple trick follow the relation attribute) which should result in a single query.
Moreover, if you are uncertain of a number of queries that will hit the database, you can write a very precise test with one of the assertions, that is available since Django 1.3: assertNumQueries

Add a custom method in Django model to know if the current user is the author

I am retrieving a bunch of things with a queryset and displaying them as a list, that is then clickable to view the chosen article's details.
So in the article's details view, I have a is_creator method:
#login_required
def is_creator(userProfile, article):
if userProfile == article.creator:
return True
else:
return False
So I can display an edit button at will.
On the homepage though, it's a different story because I'm making a query, and giving the queryset directly to the template that will make a for loop to display the titles. I still want to know for each article if the current user is the creator though.
So I'm thinking of adding the work in the model itself, not to have to duplicate code anywhere.
#property
def is_creator(self,user):
if self.creator.user == user:
return 1
else:
return 0
I was thinking that by adding that in the model, I should be able to call in the template {% if event.is_creator user %}test{% endif %} pretty easily. Seems that I'm wrong, because I'm facing:
TemplateSyntaxError at /
Unused 'user' at end of if expression.
I'm coming from the PHP world so it feels like this should work, but I'm obviously doing something wrong.
Thanks in advance :)
EDIT: I'm guessing that another solution would be in the view to loop through the Queryset with something like:
variables['articles'] = Event.objects.filter(
(Q(creator=me) | Q(bringing__attendee=me)) & Q(date_start__lt=datenow) & Q(date_end__gt=datenow)
).order_by('-date_start')
for article in variables['articles']:
article.iscreator=1 (I can do some more work here)
But it seems like having to loop over the QS is not the best idea.
It is very sad but you cant pass params to methods from templates(indeed this is a good idea - so you don't mix presentation logic with model logic, almost :) ). You have to write a template tag for this purpose.
https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
tag would look like this(not tested):
#register.simple_tag(takes_context=True) # assuming you are running in request context
def current_user_is_creator(context,article):
user = context['request'].user
return article.creator.user == user # dont forget to add proper checks
Or you could prepare required data in the view.

django wizard, using form and formset in the same step

I have a scenario which I'm trying to plan to start coding and I'm thinking to use django wizard.
My plan is to build a django wizard with two steps, the first simple but the second a bit more complicated. The second step will contain a form that will reshape based on value selected from the first step, which I can see myself doing. I already explored all the existing functionality and I think it can be done easily.
The challenge I'm facing though, is that in the second step itself. I have a form and a formset, the formset is one to many to form (Article -> Images) so when reaching the second step the user will be able to upload one or more images to the same article.
I tried to search everywhere on google mailing lists and stackoverflow for passing a formset to the django wizard class but it seems like you can not pass two forms in the same step.
NewItemWizard.as_view([
('category', CategorySelectionForm),
('article', ArticleForm)
])
as seen, in the example code above, I would like to be able to pass both ArticleForm and ImageFormset to the second step. Is there a way to do this out of the box?
Based on what I'm reading, I believe using a function like get_context_data could help, but it will be very hacky.
def get_context_data(self, form, **kwargs):
context = super(NewItemWizard, self).get_context_data(form=form, **kwargs)
if self.steps.current == 'article':
context.update({
'image_formset': ImageFormset()
})
return context
Anyone can advise for a better approach?
Cheers,
There is no straight API to do it yet, but there is a clean solution like the one mentioned here:
https://code.djangoproject.com/ticket/18830

Django admin for User's objects

I would like to allow a User to have an admin interface to their own Video objects. I was planning on writing some views that allowed things like setting attributes like "published" or deleting objects.
I've started looking into using django's Admin site - but that seems like it might be overly complicated for what I want (just deleting/ setting the published attribute).
Is one approach better than the other? Writing something from scratch or using the Admin site?
If I were to write something from scratch - what is the correct way to achieve ModelAdmin style actions (ie. delete_selected(queryset, request))
This is exactly what the admin should be used for! How could it be too complicated? Even writing a handful of lines of HTML would take longer.
If you built this yourself, no matter how simple, you'll have to define views that list objects, validate input, check permissions, write HTML, implement some kind of multiple action system that maps to python code, ....
Assuming you don't want to do that:
You're going to want to look into making multiple admin sites and filtering admin results to only those that belong to the user via overriding the queryset method on a ModelAdmin
# pasted from docs
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)

Limiting Django ModelChoiceField queryset to selected items

Here is what I've been struggling for a day...
I have a Message model in which recipients is a ManyToManyField to the User model.
Then there is a form for composing messages. As there are thousands of users, it is not convenient to display the options in a multiple select widget in the form, which is the default behavior. Instead, using FcbkComplete jquery plugin, I made the recipients field look like an input field where the user types the recipients, and it WORKS.
But...
Although not visible on the form page, all the user list is rendered into the page in the select field, which is something I don't want for obvious reasons.
I tried overriding the ModelChoiceField's behavior manipulating validation and queryset, I played with the MultipleChoice widget, etc. But none of them worked and felt natural.
So, what is the (best) way to avoid having the whole list of options on the client side, but still be able to validate against a queryset?
Have you seen django-ajax-selects? I've never used it, but it's in my mental grab bag for when I come across a problem like what it sounds like you're trying to solve...
I would be trying one of two ways (both of which might be bad! I'm really just thinking out aloud here):
Setting the field's queryset to be empty (queryset = Model.objects.none()) and having the jquery tool use ajax views for selecting/searching users. Use a clean_field function to manually validate the users are valid.
This would be my preferred choice: edit the template to not loop through the field's queryset - so the html would have 0 options inside the select tags. That is, not using form.as_p() method or anything.
One thing I'm not sure about is whether #2 would still hit the database, pulling out the 5k+ objects, just not displaying them in the html. I don't think it should, but... not sure, at all!
If you don't care about suggestions, and is OK to use the ID, Django Admin comes with a raw_id_field attribute for these situations.
You could also make a widget, that uses the username instead of the ID and returns a valid user. Something among the lines of:
# I haven't tested this code. It's just for illustration purposes
class RawUsernameField(forms.CharField):
def clean(self, value):
try:
return User.objects.get(username=value)
except User.DoesNotExist:
rause forms.ValidationError(u'Invalid Username')
I solve this by overriding the forms.ModelMultipleChoiceField's default widget. The new widget returns only the selected fields, not the entire list of options:
class SelectMultipleUserWidget(forms.SelectMultiple):
def render_options(self, choices, selected_choices):
choices = [c for c in self.choices if str(c[0]) in selected_choices]
self.choices = choices
return super(SelectMultipleUserWidget,
self).render_options([], selected_choices)
class ComposeForm(forms.Form):
recipients = forms.ModelMultipleChoiceField(queryset=User.objects.all(),
widget=SelectMultipleUserWidget)
...