How to disallow admin to change instance in Django admin? - python

I have a model Order and model Invoice. The Order has
invoice = models.OneToOneField('Invoice', related_name='order', on_delete=models.CASCADE, blank=True, null=True)
Invoice object is created right after order object is created an assigned to it. Admin has to edit the invoice (price field) before customer pays.
The problem is that Django-admin allows admin to change this field too (bottom of the image), which I can't risk but I want to let the pencil icon (change attributes of the invoice).
Is it possible to do that? When I add invoice to readonly_fields in OrderAdmin, Admin can't edit those attributes like invoice.price etc.
EDIT:
So I want admin to be able to edit attributes of the invoice. Not add nor choose from dropdown.

One option would be to provide a custom template for this view. The docs say that you can specify a path to a custom template using ModelAdmin.change_form_template.
Here is a section of the docs that talk about how to override a template: https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#overriding-vs-replacing-an-admin-template
Though this is not the optimal solution, you could probably use Javascript to hide/disable the parts you don't want.
Finally, you may want to consider your usage of the Django admin:
The admin’s recommended use is limited to an organization’s internal
management tool. It’s not intended for building your entire front end
around.
The admin has many hooks for customization, but beware of trying to
use those hooks exclusively. If you need to provide a more
process-centric interface that abstracts away the implementation
details of database tables and fields, then it’s probably time to
write your own views.

def has_add_permission(self, request):
return False
Django admin: How to display a field that is marked as editable=False' in the model?

Related

How to add separate permission for a selected ModelAdmin in Django wagtail just like 'Page permissions'?

I am creating an application for teaching management in Wagtail. I create an AdminModal for 'Subjects'. I want to allow only selected user group to access a selected subject. Just like "Page permissions" in 'Add group'. Any idea how to do that?
You can do this by overriding get_queryset method in the ModelAdmin class that is associated with the Subject model.
def get_queryset(self, request):
qs = super().get_queryset(request)
valid_subjects = ['subject1', 'subject2', 'subject3']
return qs.filter(subject_name__in=valid_subjects)
The above example shows a way to restrict users from subject name. If you need to access group, you need to have a relation between Subject model and Group model. Then all you have to do is change the filter query.
You can find more about querying in this documentation.
First, I am assuming when you say AdminModal, you actually mean a ModelAdmin model for subjects. If that is not the case, can you please show me where AdminModal comes from?
The subject will need a field you can use to identify which group should be able to manage it. Then you will need to customize the admin interface for your Subjects model. In particular you will need to enforce some permissions - and create some custom get_queryset methods so users are only shown Subjects they should be able to edit. Start with this part of the documentation and post back when you have more specific questions: https://docs.wagtail.org/en/stable/reference/contrib/modeladmin/primer.html

How to add a conditional field in a Django model?

Basically, what I want is a field to be available if a condition is met, so something like this:
class ConditionalModel(models.Model):
product = models.ForeignKey(product, on_delete=models.CASCADE)
if category == "laptop":
cpu_model = models.CharField(max_length=200)
so if I were to go to the Django admin page and create an instance of the model and then choose "laptop" as the product from the drop-down list of existing "products", a new field would be available. I couldn't find anything about this in the documentation, so I'm wondering whether it's even possible.
What you are asking for is not "technically" possible. A model relates a database object, and under traditional SQL rules, this isn't possible. You could instead make that field optional, and then customize the admin page's functionality.
Another potential option, though I do not have much experience with it, would be to use a NoSQL database in the case where you don't want to store NULL values in your db.
I do not think it is possible because models defines databases tables so the column has to be present.
You can use the keyword blank=True to allow an object without this field.
Maybe you can customize the admin interface to hide the field in some cases.
You can't do that in models.
You can hide it in admin panel or you can make separate model for laptop.
Or you can make field blank=True
Making a field optional is not possible but you can use a generalized model called Product and two or more specialized ones called for example : ElectronicProduct that contains the field cpu_model and NonElectronicProduct, the two specialized models have to contain a OneToOneField to the Product model to ensure inheritance.

What is the recommended approach to implement admin-actions-alike functionality outside admin?

I've been searching a way to reproduce admin-actions behavior on my own tables using django-tables2. I haven't found any module to introduce this functionality to a ListView to derive from it and looking at ModelAdmin I see there are many methods implied on this.
Of course, I can add a form around my table to get the checkboxes and a submit button pointing to a view that works with the ids but I'm looging to get a combo to choose among different actions as in django-admin but also to have that 'actions' meta option to list some methods as the possible actions to perform.
I found django-actions which is still very young but also it introduces it's own page for operations and I just need to integrate functionality on my own model so I can connect some input type=select with the model actions.
Any comment is appreciated :)
There is no built-in solution for it. You have to implement your actions in your views and the functionality to your templates.
Add, edit and delete operations are very easy to implement in your views.py. This depends on your models, but you can trigger database manipulations from within your templates and keep the logic in your views.py.
You can also easily add a form to your templates as it is described in the docs:
# forms.py
from django.forms import ModelForm
from myapp.models import Article
# Create the form class.
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['pub_date', 'headline', 'content', 'reporter']
model := Choose your model which you want to modify / add
fields := Select some fields from your model, which you want to show up in your form
This defines a form corresponding to your model, which can be used in your templates to modify or add an entity to your database.

Multiple versions of django admin page for the same model

In my django admin section, I'd like to show different versions of the admin page depending on what kind of user is currently logged in. I can think of a couple ways this might work, but haven't figured out how to do any of them.
Perhaps I could put logic into the admin.ModelAdmin to look at the current user and change the 'exclude' field dynamically. Does that work? Or maybe run different custom templates based on who's logged in, and have the templates include / exclude the fields as appropriate.
I could register two versions of the admin.ModelAdmin class, one for each type of user, and maybe restrict access through permissions? But the permissions system seems to believe fairly deeply in one set of permissions per model class so I'm not sure how to change that.
I could grab a couple of the widgets that are used in rendering the admin page templates, and include them in my own page that does the one specific job I need powerful users to be able to do.
I could set up multiple AdminSites and restrict access to them through the url / view system. But then I'm not sure how to register different admin.ModelAdmin classes with the different AdminSites.
Any advice on this would be appreciated.
Answer
Thanks for the hint. Here's how I did it...
def get_form(self, request, obj=None, **kwargs):
"""This dynamically inserts the "owners" field into the exclude list
if the current user is not superuser.
"""
if not request.user.is_superuser:
if self.exclude:
self.exclude.append('owners')
else:
self.exclude = ['owners']
else:
# Necessary since Admin objects outlive requests
try:
self.exclude.remove('owners')
except:
pass
return super(OwnersModelAdmin,self).get_form(request, obj=None, **kwargs)
There are quite a few hooks provided in the ModelAdmin class for this sort of thing.
One possibility would be to override the get_form method. This takes the request, as well as the object being edited, so you could get the current user from there, and return different ModelForms dependent on the user.
It's worth looking at the source for ModelAdmin - it's in django.contrib.admin.options - to see if overriding this or any other other methods might meet your needs.

Filter a User list using a UserProfile field in Django Admin

I'm trying to filter the User list in Django using a UserProfile Field... I need to implement a queue system where new users are put in a queue until an Admin approves them.
I simply added a is_in_queue boolean field to my UserProfile model... However, when displaying the user list in my Admin area, I realized that you can't filter the list using a Model's foreign key field (in this case, a field of UserProfile)
Apparently, list_display items can be callables but list_filter can't, so I can list IF a user is in the queue without a problem, but the admin would have to scroll through the whole user list to spot which ones are in the queue which makes no sense... Filtering only users that are in the queue (using userprofile.in_queue) would be much more practical...
Finally, I thought about adding a custom view to my admin area that would list only the user in the queue, but that custom view does not show up on the Admin area Index page, and putting together a whole new AdminSite only for a new filtering option seems a bit over the top...
So basically to sum it up: Can I filter my User list based on a
UserProfile field? If not, can I add a custom view that's accessible
from the front page without having to create a completely new
AdminSite only for that?
Django 1.3 fixed that - list_filter now allows to span relations:
https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
You may want to take a look in to using a custom manager for the admin_objects of your model.
class UserAdminManager(models.AdminManager):
"""
Custom manager for the User model.
"""
def get_query_set(self):
"""
Overwrites the get_query_set to only return Users in the queue.
"""
return super(UserAdminManager, self).get_query_set().filter(userprofile__queue=True)
By overwriting the get_query_set method you can filter the results. Then just assign this to the admin_objects property of your User model.
admin_objects = UserAdminManager()
Some of the property names in my example may be wrong, as I don't know your model setup, but hopefully you get the idea.
You can research this further by checking out the django docs and searching for "custom managers".
It sounds to me like the quickest and easiest option is to add a new admin view to your application, specifically for your custom user model. See the Django admin docs for details, though it sounds like you know how to use Admin already.
Once the admin page is specific to your model, all your custom fields will no longer be foreign keys. This would make filtering easy.

Categories