How to reuse existing admin models to display a custom Queryset? - django

I'm writing my first application in Django, and now that the base is covered, I try to enhance a bit the admin part to ease up my life.
I have two classes in my model:
class Puzzle(models.Model):
puzzle_pieces = models.ForeignKey(PuzzlePieces,on_delete=models.CASCADE)
class PuzzlePieces(models.Model):
puzzle_pieces = models.CharField(max_length=255, default='empty')
with admin models in place too:
class PuzzlePiecesAdmin(admin.ModelAdmin):
class PuzzleAdmin(admin.ModelAdmin):
I want to define in the PuzzlePiecesAdmin class a custom action to display (reusing the format defined in PuzzleAdmin) all the puzzles which are linked to the selected puzzle_pieces
I know how to create custom actions,
def show_related_puzzles(modeladmin, request, queryset):
I've seen on the internet different ways to filter directly within the PuzzleAdmin class,
but not how to set the queryset from the outside.
But I don't understand how to launch the display of an instance of PuzzleAdmin limited to the queryset I will define within show_related_puzzles.
Could anyone explain me how to proceed?
Thanks in advance

OK, I found the start of an answer in the following:
https://stackoverflow.com/a/1652377/12505071
the standard changelist view accepts normal queryset filter parameters as GET arguments. So you can do:
/admin/puzzles/puzzle/?puzzle_pieces__pk=21
I finally found out by trial and error how to add another filter:
/admin/puzzles/puzzle/?difficulty__id__exact=35&puzzle__pk=560
Could anybody tell me how to add a second value for the same parameter?

Related

Is there a way to add more than one model to a class based view?

Is there a way to add more than one model to a class based view ?!
This is my detail view.
class ArticleDetailView(DetailView):
model = Article
I want to add another model to my view .
I know i can use function based views in order to to have multiple models in my view.
But i'm wonder that is there any way i can do this with my class based view ?
Yes, but you have to handle excess models yourself. If you want to show details of two separate models in one view, you will need to override get_context_data. Of course you will need to write your custom methods to fetch the object.
If it is ambiguous to determine which model is main model (meaning that the majority of the view revolves around it and the other models are just supplementary, for example: a user profile needs user model and may need other models such as posts or favorite post etc., In this case the main model is user model), you should really use TemplateView to handle all of them yourself, instead of DetailView or ListView.
Check out this site, which has plenty of info to see what methods does class methods have and how to override them.

Get an url pk in a generic RESTful view?

I'm trying to manage my REST API like that :
http://xxx/users/userid[0-9]+/projects/projectid[0-9]+/tasks/taskid[0-9]+/
So I can access the JSON easily in my website. But, the thing is, I defined my view classes using the REST framework generic views. For example, here is my UserDetail view :
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
But of course I don't want all my users to be displayed, I just want my user with the ID userid to be displayed. I don't really know how to do it, I tried
queryset = User.objects.filter(id=userid)
but of course userid is not defined... Any help please ?
Edit : just to clarify, here is the url that leads to this view :
url(r'^users/(?P<pku>[0-9]+)/$', views.UserDetail.as_view(
), name='user-detail'),
First of all, If you want to use class based views, you should read some more about them. They are thoroughly explained on Django's docs and you can read about the specific generics of the framework you're using on the REST framework docs too. I'm not saying that you haven't read those, just that you seem to be missing some basic concepts that are explained there.
Now, to the problem at hand, if you look at the doc's of the generic view you're extending, you can see that it represents the endpoints for a single instance, meaning that it won't act on all your model's instances (as you seem to assume).
Also, you can see that this view is built on top of a series of other classes, the more critical one being GenericAPIView. On there you can see two things:
The queryset class field in this context is meant for filtering the posible instances you can manipulate, not obtaining the specific instance described on your url.
The lookup_field is the field that defines which attribute from your model will be used for getting the actual instance. You should define this field to whatever field you're going to use on your url to identify your object (generally it's pk). It's also important to note that the url should include a keyword argument corresponding to this value.
Internally, the view will take care of calling the get_object method, which usese the lookup_field value to find the specific model, and then feed that object to the serializer and return the result back to the client.
DISCLAIMER: I've never used Django REST framework, I put this answer togheter by reading the relevants docs and based on my experience.
I guess you need this:
Resolve function (django 1.4)
Then in your view class method you can do:
temp1, args, kwargs = resolve(self.request.path)

Using Django filters inside model function

The main purpose of a model is to contain business logic, so I want most of my code inside Django model in the form of methods. For example I want to write a method named get_tasks_by_user() inside task model. So that I can access it as
Tasks.get_tasks_by_user(user_id)
Following is my model code:
class Tasks(models.Model):
slug=models.URLField()
user=models.ForeignKey(User)
title=models.CharField(max_length=100)
objects=SearchManager()
def __unicode__(self):
return self.title
days_passed = property(getDaysPassed)
def get_tasks_by_user(self,userid):
return self.filters(user_id=userid)
But this doesn't seems to work, I have used it in view as:
tasks = Tasks.objects.get_tasks_by_user(user_id)
But it gives following error:
'SearchManager' object has no attribute 'get_tasks_by_user'
If I remove objects=SearchManager, then just name of manager in error will change so I think that is not issue. Seems like I am doing some very basic level mistake, how can I do what I am trying to do? I know I can do same thing via :Tasks.objects.filters(user_id=userid) but I want to keep all such logic in model. What is the correct way to do so?
An easy way to do this is by using classmethod decorator to make it a class method. Inside class Tasks:
#classmethod
def get_tasks_by_user(cls, userid):
return cls.objects.filters(user_id=userid)
This way you can simply call:
tasks = Tasks.get_tasks_by_user(user_id)
Alternatively, you can use managers per Tom's answer.
To decided on which one to choose in your specific case, you can refer James Bennett's (the release manager of Django) blog post on when to use managers/classmethod.
Any methods on a model class will only be available to instances of that model, i.e. individual objects.
For your get_tasks_by_user function to be available as you want it (on the collection), it needs to be implemented on the model manager.
class TaskManager(models.Manager):
def get_tasks_by_user(self, user_id):
return super(TaskManager, self).get_query_set().filter(user=user_id)
class Task(models.Model):
# ...
objects = TaskManager()

django: Admin interface for an extended class

I have two classes for which I want an admin interface. But one class extends the other. Ex
class Questions(models.Model):
pass
class MathQuestion(Questions):
some fields ....
Now the simplest way is to create a separate admin for MathQuestion. However, is there a possibility that I can have MathQuestion displayed in a more intuitive fashio in Admin, ex: When user goes to QuestionAdmin interface and selects a further type for Maths.
Add extra field 'question_type' with default value of None and not nullable (so you can be sure Question can't have instances, not sure if it works)
overload the save method in child class and assign there some value to question_type, meaning MathQuestion.
Use filtering in admin as always.

Django: Is there a way to add a model instance method to the admin?

I'm looking to add a method that my Ticket model has called process to the admin, so that I could click a link in the list view, and "process" my model instance (do an API call behind the scenes).
To clarify:
class Ticket(models.Model):
title = models.CharField(max_length=255)
def process(self):
... hardcore processing action ...
I need to add the process() method directly to the admin without using a separate function.
You just need to provide a small method in your ModelAdmin class that returns a link pointing at a view that calls your model method, and add the name of that method to the modeladmin's list_display tuple. You'll obviously also need to define this view itself, and a url that points to it.
Yes, thats possible; Check out this documentation, just what you need:
http://docs.djangoproject.com/en/1.1/ref/contrib/admin/actions/#ref-contrib-admin-actions