Django Inline Admin delete missing - python

Here is my admin.py
class BooksInlineAdmin(admin.TabularInline):
model = Book
extra = 2
max_num = 4
def has_delete_permission(self, request, obj):
return True
class AuthorAdmin(admin.ModelAdmin):
list_display=("name", "phone")
inlines = [BooksInlineAdmin]
admin.site.register(Author, AuthorAdmin)
In admin the auto generated inlines are missing "delete". If I add one by clicking "add another" It is having delete button. why is it so? How can i enable delete for all the inline forms. (I am using django 1.6)

The extra=2 means you always see at least 2 inlines for this model,
So delete button is removed, try changing the extra value

Related

Ignore inline model when saving

I've been looking on the documentation and stackoverflow/forums for a way to ignore the inline children of a model when I save it in django admin. I've been searching for a few days and I can't seem to find an answer.
I have a normal tabularinline object:
class UserOrdersAdmin(admin.TabularInline):
model = Order
classes = ['collapse']
And a normal User admin registration:
class UserAdmin(BaseUserAdmin):
inlines = (UserOrdersAdmin, UserSettingsAdmin)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
What I simply want is when I press save within the User "change view", it will ignore the inline "UserOrderAdmin" which is inline to UserAdmin.
From your response on my comment I am getting the idea you want to show some extra information in the admin, that is not editable. This can be achieved using readonly_fields in the Inline, for completeness you should also set the max_num to 0, because otherwise you can add empty inlines.
You could enter all fields manually or use something like given in this answer: https://stackoverflow.com/a/42877484/2354734
The end result would look something like this.
class UserOrdersAdmin(admin.TabularInline):
model = Order
classes = ['collapse']
max_num = 0
def get_readonly_fields(self, request, obj=None):
return list(set(
[field.name for field in self.opts.local_fields] +
[field.name for field in self.opts.local_many_to_many]
))
For completeness of the answer also a link to the documentation
Try this:
class UserOrdersAdmin(admin.TabularInline):
model = Order
classes = ['collapse']
extra = 0

How to use admin.ModelAdmin to customize the "add" popup?

The story begins with 2 models (User and ClientAccount) which are linked with an extra M2M model.
It is possible to create ClientAccount during editing User. The page will show a popup which allow you create a new ClientAccount. But the problem is: can I disable the foreign key fields of ClientAccount which is linking to User? This is quite confusing.
Code here:
class User(models.Model):
client_accounts = models.ManyToManyField('ClientAccount', related_name='+', through='UserClientAccountM2M', through_fields=('user', 'client_account'))
class ClientAccount(models.Model):
users = models.ManyToManyField('User', related_name='+', through='UserClientAccountM2M', through_fields=('client_account', 'user'))
class UserClientAccountM2M(models.Model):
user = models.ForeignKey(User, db_column='user_id')
client_account = models.ForeignKey(ClientAccount, db_column='client_id')
class UserAdmin(TimeLimitedAdmin):
class ClientAccountInline(admin.TabularInline):
model = ClientAccount.users.through
inlines = [
ClientAccountInline,
]
class ClientAccountAdmin(TimeLimitedAdmin):
class UserInline(admin.TabularInline):
model = ClientAccount.users.through
inlines = [
UserInline,
]
admin.site.register(User, UserAdmin)
I realise this is an old question, but I came across it as the most relevant question whilst looking to solve a similar problem (hide a FK inline only in the add popup). I'm sure you have left this problem well behind by now, but maybe others will find it useful.
I added the below to the ModelAdmin class that the popup is for. I check the request for the GET params only present when the popup dialog is launched, and if they are there, I loop through the inlines and remove the one I don't want.
def get_inline_instances(self, request, obj=None):
inline_instances = super().get_inline_instances(request, obj=None)
if '_to_field' in request.GET and '_popup' in request.GET:
# Popup dialog is open.
unwanted_inline = None
for inline in inline_instances:
inline_model_name = inline.opts.model.__name__
if inline_model_name == 'UnwantedModelName':
unwanted_inline = inline
if unwanted_inline:
inline_instances.remove(unwanted_inline)
return inline_instances
In your case, I would add the above to ClientAccountAdmin, removing the UserInline.
If you simply want to hide the m2m fields in the ClientAccount you can remove the line in the Admin.py
because there you explicitely say it should show the connection to the user in a tabularInline:
class ClientAccountAdmin(TimeLimitedAdmin):
class UserInline(admin.TabularInline):
model = ClientAccount.users.through
#inlines = [UserInline,]
There you say explicitely you want to have the M2M field from ClientAccount to User in an Inline, which you do not want. Get rid of it and the fields will disappear
EDIT:
The problem is that the "add..." link will always refer to the .../ClientAccount/add/?_popup=1 page which uses the default admin view for this model.

Django inline link to model editing

I know this issue has been asked more than once, but as Django is evolving with new version, I'll ask the question again :
I am using the model User (Django User, not in my models.py) and create another model with a Foreign key to User.
models.py :
class Plan(models.Model):
user = models.ForeignKey(User)
I can simply display every Plan in my user by doing this in admin.py :
class PlanInline(admin.TabularInline):
model = Plan
extra = 0
class MyUserAdmin(UserAdmin):
ordering = ('-date_joined', 'username')
inlines = [PlanInline,]
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
But things are about to get more tricky. I want to add a model that has a foreign key pointing to Plan :
class Order(models.Model):
plan = models.ForeignKey('Plan')
And I want to be able to see all Orders for each Plan. As of today, it is impossible to have nested inlines in Django Admin (without editing the HTML, which I want to avoid) :
User
-> Plan 1
-> Order 1
-> Order 2
-> Plan 2
-> Order 3
So my idea is to display in the User Admin only A LINK for each plan, to the page to edit Plans, and put Orders as inline :
class OrderInline(admin.TabularInline):
model = Order
extra = 0
class PlanAdmin(admin.ModelAdmin):
inlines = [OrderInline,]
admin.site.register(Plan, PlanAdmin)
The question is, how do I display a link to a Plan in my User Admin?
class MyUserAdmin(UserAdmin):
ordering = ('-date_joined', 'username')
??? LINK ????
I saw some solutions on this topic : Django InlineModelAdmin: Show partially an inline model and link to the complete model, but they are a bit "dirty' as they make us write HTML and absolute path into the code.
Then I saw this ticket on Djangoproject : https://code.djangoproject.com/ticket/13163. It seems exactly what I'm looking for, and the ticket is "fixed". So I tried adding like in the fix show_change_link = True :
class PlanInline(admin.TabularInline):
model = Plan
extra = 0
show_change_link = True
class MyUserAdmin(UserAdmin):
ordering = ('-date_joined', 'username')
show_change_link = True
inlines = [UserProfileInline, PlanInline]
But it doesn't work (and I have no log or error).
Is there any way to do this in a clean way?
Update for django 1.8
show_change_link = True
https://github.com/django/django/pull/2957/files
I suggest adding a custom PlanInline method that returns the link and see if it helps. Something along these lines:
from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse
class PlanInline(TabularInline):
model = Plan
readonly_fields = ('change_link',)
...other options here...
def change_link(self, obj):
return mark_safe('Full edit' % \
reverse('admin:myapp_plan_change',
args=(obj.id,)))
Basically all we do here is create the custom method that returns a link to the change page (this specific implementation is not tested, sorry if there is any parse error but you get the idea) and then add it to the readonly_fields as described here: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields
A couple of notes for the change_link method: You need to replace 'myapp' in the view name with your actual application name. The mark_safe method just marks the text as safe for the template engine to render it as html.

Django Admin - Make previous inline forms uneditable

I have an inline form in Django Admin. When the user edits the modelform all previously filled inline forms are also listed. I just want to allow users to view previously filled inline forms and make them uneditable. But the user can add another form.
I tried using editable=False but this doesn't allow me to fill new form.
I think https://code.djangoproject.com/ticket/15602 prevents you from doing what you want.
If you split it into two inline admins, one for listing and one for adding, you can achieve what you want, but I don't like this solution:
class CommentListInline(admin.TabularInline):
model = Comment
fields = ('comment',)
readonly_fields = fields
extra = 0
can_delete = False
def has_add_permission(self, request):
return False
class CommentAddInline(admin.TabularInline):
model = Comment
fields = ('comment',)
extra = 1
can_delete = False
def has_change_permission(self, request, obj=None):
return False
class PageAdmin(admin.ModelAdmin):
inlines = [CommentListInline, CommentAddInline]

How can I rename a column label in Django Admin for a field that is a method//property?

I am trying to redefine my admin page for the auth.User model.
Everything is working properly, except for one thing. Check the code below:
from django.contrib import admin
from django.contrib.auth.models import User
from access.models import UserProfile
class UserProfileInline(admin.StackedInline):
model = UserProfile
class UserAdmim(admin.ModelAdmin):
inlines = [UserProfileInline,]
list_display = ['id', 'username', 'get_full_name', 'email']
admin.site.unregister(User)
admin.site.register(User, UserAdmim)
As you can see, one of the fields I want to be displayed in the model page listing -- defined by list_display -- is get_full_name. The problem is that the column label in the admin is displayed as Get full name.
My question is simple: can I override this? If so, how?
Thanks for your help.
Set an attribute in your function called short_description to your desired label in your model definition.
# note, this must be done in the class definition;
# not User.get_full_name.short_description
get_full_name.short_description = 'my label'
Alternatively, if you don't want to pollute your model with admin specific code, you can set list_display to a method on the ModelAdmin which takes one argument: the instance. You'll also have to set readonly_fields so that the admin doesn't try to look up this field in your model. I prefix admin fields with _ to differentiate.
class MyAdmin(...):
list_display = ('_my_field',)
readonly_fields = ('_my_field', )
def _my_field(self, obj):
return obj.get_full_name()
_my_field.short_description = 'my custom label'
Update:
Note that this will break default admin ordering. Your admin will no longer sort fields by clicking the label. To enable this functionality again, define an admin_order_field.
def _date_created(self, obj):
return obj.date_created.strftime('%m/%d/%Y')
_date_created.short_description = "Date Created"
_date_created.admin_order_field = 'date_created'
Update 2:
I've written an admin method decorator that simplifies this process, because once I started using highly descriptive verbose method names, setting attributes on the function became massively repetitive and cluttering.
def admin_method_attributes(**outer_kwargs):
""" Wrap an admin method with passed arguments as attributes and values.
DRY way of extremely common admin manipulation such as setting short_description, allow_tags, etc.
"""
def method_decorator(func):
for kw, arg in outer_kwargs.items():
setattr(func, kw, arg)
return func
return method_decorator
# usage
class ModelAdmin(admin.ModelAdmin):
#admin_method_attributes(short_description='Some Short Description', allow_tags=True)
def my_admin_method(self, obj):
return '''<em>obj.id</em>'''

Categories

Resources