I have a model with auto_now, and auto_now_add set for Update and Create fields:
class HotelProfiles(models.Model):
fe_result_id = models.AutoField(primary_key=True)
fe_created_date = models.DateTimeField(verbose_name='Created',
blank=True,
auto_now_add=True)
fe_updated_date = models.DateTimeField(verbose_name='Updated',
blank=True,
auto_now=True)
In the Admin it displays both fields but leaves them uneditable. They
don't seem to be passed to my form to be rendered. I don't want them
to be editable, but I would like to display at the top of my form.
How can I do this?
This is in my HotelProfilesAdmin class:
readonly_fields = ('fe_result_id', 'fe_created_date', 'fe_updated_date', 'fe_owner_uid')
#date_hierarchy = 'lto_end_date'
fieldsets = (
("Internal Use Only", {
'classes': ('collapse',),
'fields': ('fe_result_id', 'fe_created_date', 'fe_owner_uid', 'fe_updated_date', 'fe_result_status')
}),
Make the fields you want readonly
explicitly override what fields are available in this admin form (readonly fields will be present but readonly)
Example:
from django.contrib import admin
class HotelProfilesAdmin(admin.ModelAdmin) :
# Keep the fields readonly
readonly_fields = ['fe_created_date','fe_updated_date']
# The fields in the order you want them
fieldsets = (
(None, {
'fields': ('fe_created_date', 'fe_updated_date', ...other fields)
}),
)
# Add your new adminform to the site
admin.site.register(HotelProfiles, HotelProfilesAdmin)
For the benefit of others, I figured out a way to do this. I'm new to Django, so if there is a better way, I'd be interested in hearing it. The view code is below. I wasn't sure if Django was not returning the fields from the query, and I found out that it was. So, something in the renderering of the form that I don't understand removed those fields so they couldn't be rendered. So, I copied them to a dict called read_only before rendering and passed it along.
try:
hotel_profile = HotelProfiles.objects.get(pk=hotel_id)
read_only["created_on"] = hotel_profile.fe_created_date
read_only["updated_on"] = hotel_profile.fe_updated_date
f = HotelProfileForm(instance=hotel_profile)
#f.save()
except:
f = HotelProfileForm()
print 'rendering blank form'
return render_to_response('hotels/hotelprofile_form.html', {'f' : f, 'read_only': read_only}, context_instance=RequestContext(request))
Related
I have following model:
class Model(models.Model):
creator = models.ForeignKey(User,related_name='com_creator',on_delete=models.SET_NULL, blank=True, null=True)
username = models.CharField(max_length=60,default="")
created = models.DateTimeField(auto_now_add=True)
body = models.TextField(max_length=10000,default=" ")
subtype = models.CharField(_("SubType"),max_length=100)
typ = models.CharField(_("Type"),max_length=50)
plus = models.ManyToManyField(User,related_name='com_plus', verbose_name=_('Plus'), blank=True)
is_anonymous = models.BooleanField(_('Stay Anonymous'), blank=True, default=False)
The values in typ and subtype are codes, like: "6_0_p", (because the original values are ridiculosly long, so I use codes and a dict to translate to human readable form).
QUESTION: How can I intercept these values in django admin and translate them to human readable form?
This is what i tried so far:
class ModelAdmin(admin.ModelAdmin):
model = Model
extra = 1
exclude = ("username","creator","plus" )
readonly_fields = ('subtype','typ','is_anonymous','created')
fields = ('body','subtype','typ')
def typ(self, obj):
self.typ = "ppp"
obj.typ = "ppp"
return "pppp"
I tried returning the object, self, some other value. I also tried to set the value without using a callable just declaring "typ='xxx'". Nothing. I probably don't understand how this whole thing works ...
Any ideas will be appreciated.
You need to create a readonly_field which corresponds to your method name, then return anything from that method.
Documentation here:
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields
class MyAdmin(admin.ModelAdmin):
readonly_fields = ('my_custom_field',)
def my_custom_field(self, obj):
return 'Return Anything Here'
I think that your code doesn't work because the method name is the same as the field.
You have to change the name of field, like _typ in fields tuple, then add a method called _typ that return anything or some obj attr.
i.e:
class ModelAdmin(admin.ModelAdmin):
model = Model
extra = 1
exclude = ("username","creator","plus" )
readonly_fields = ('subtype','typ','is_anonymous','created')
fields = ('body','subtype','_typ')
def _typ(self, obj):
return "pppp"
In Django 4 and above you can add a custom (readonly) field easily, like this:
class FooAdmin(admin.ModelAdminAbstractAdmin):
readonly_fields = [
'_dynamic_field',
]
fieldsets = [
('Form', {
'fields': (
'_dynamic_field',
),
'classes': ('form-group',)
}),
]
def _dynamic_field(self, obj=None):
if obj is not None:
return 'Bar'
return "Foo"
# to change the label
_dynamic_field.__name__ = "Dynamic field label"
How you can see, the field needs to be in readonly_fields and fieldsets too.
When clicking on original list_display item I want it to show another list_display(containing of models that have ForeignKey to original item) instead of normal fieldsets.
I can get it working but only with inlines, is it possible to display it as another list_display?
I am using Django Suit to get tabs:
class KWInline(admin.TabularInline):
model = Url
list_display = ('title', 'url', 'pda', 'upa')
extra = 0
suit_classes = 'suit-tab suit-tab-cities'
class KWAdmin(ImportExportModelAdmin):
resource_class = KWResource
list_display = ('Keyword', 'searches', 'cpc', 'comp'...)
inlines = [KWInline]
fieldsets = [
(None, {
'classes': ('suit-tab suit-tab-cities',),
'fields': ['Keyword', 'searches', 'cpc']}),
... .... ...
suit_form_tabs = (('general', 'General'), ('cities', 'Cities'),
('flag', 'Flag'), ('info', 'Info on tabs'))
I tried changing admin.TabularInline to normal admin and get expected error(calling inlines without proper class inheritance)
It looks like this, any ideas on how to change inlines to list_display?:
I'd like a boolean field to be editable in my Django admin's list display. Instead, I have uneditable icons:
My code looks like this:
# Model
class Task(models.Model):
...
is_finished = models.BooleanField()
# Admin
list_display = (..., 'is_finished')
I haven't included is_finished in the readonly_fields tuple in admin.py, so I'm surprised that it isn't editable by default. What am I doing wrong?
ModelAdmin.list_editable is what you need, see its doc here. Below you also have an example:
class TaskAdmin(models.ModelAdmin):
list_display = (..., 'is_finished')
list_editable = ('is_finished',) # this MUST only contain fields that also are in "list_display"
#list_display_links = ('foo', 'bar') # this MUST NOT contain a field in common with "list_editable"
I can not figure out why a clean_field() method is not being called for an inline form that is updated on an admin view. The code I have seems straight-forward (see synopsis below).
When I modify the Primary form through admin interface (http://admin/..../primary/1/), as expected, I see:
Admin.PrimaryAdminForm.clean_myfield() called
Admin.PrimaryAdminForm.clean() called
Model.Primary.clean() called
However, when I modify the Primary as seen as an inline on the Admin view of Membership (http://admin/..../membership/1/), I only see:
Model.Primary.clean() called
I have tried placing the "def clean_myfield(self):" method in the following locations but can not see it get executed from the Membership inlined Primary form:
Model.Primary.clean_myfield
Admin.PrimaryAdmin.clean_myfield
Admin.PrimaryAdminForm.clean_myfield
Admin.PrimaryAdminInline.clean_myfield
Is there somewhere else this clean_myfield code should be placed?
I have read (and reread) the Django docs on [forms and field validation][docs.djangoproject.com/en/dev/ref/forms/validation/#form-and-field-validation] which gives a great coverage, but there's nothing on inline validation. I've also read docs.djangoproject.com/en/dev/ref/contrib/admin/#adding-custom-validation-to-the-admin, but no help for inline specific validation. Is there other documentation on this?
---> Answered by Austin provided a doc reference to: "If not specified" (see his link) , which implies the answer. I added a request to improve the documents on this topic.
After further experimenting I found a workaround by putting code in the Model.Primary.clean() method:
def clean(self):
data = self.myfield
data += "_extra" # not actual cleaning code
self.myfield = data
So the question remains: Why is Model.clean() seem to be the only place to put admin inline form validation and not in a clean_myfield(self) method?
---> Answered by Austin. I needed add form = PrimaryAdminForm to PrimaryInline. With this addition, PrimaryAdminForm.clean_myfield(self) is called when PrimaryInline myfield is updated on Membership form. Code ordering was updated due to the added form reference.
Code synopsis:
No forms.py file -- all models are updated through admin interface
models.py:
class Membership(models.Model):
name = models.CharField( max_length=NAME_LENGTH,
null=True, blank=True, verbose_name="Membership Name Tag",
help_text="Name of membership" )
class Primary(models.Model):
user = models.OneToOneField(User, verbose_name="User Name")
membership = models.OneToOneField(Membership, verbose_name="Membership Name")
myfield = models.CharField("My Field", max_length=20, null=True, blank=True)
# clean method altered as in Update comment
# Why must this be here? Why not in clean_myfield(self)
def clean(self):
data = self.myfield
data += "_extra" # not actual cleaning code
self.myfield = data
admin.py:
class MembershipAdminForm(ModelForm):
class Meta:
model = Membership
class PrimaryAdminForm(ModelForm):
class Meta:
model = Primary
def clean_myfield(self):
data = self.cleaned_data['myfield']
data += "_extra" # not actual cleaning code
return unicode(data)
def clean(self):
cleaned_data = super(PrimaryAdminForm, self).clean()
# not actual cleaning code
return cleaned_data
# EDIT2: Moved PrimaryInline so it's defined after PrimaryAdminForm
class PrimaryInline(admin.StackedInline):
model = Primary
form = PrimaryAdminForm #EDIT2 as recommended by Austin
raw_id_fields = ['user']
verbose_name_plural = 'Primary Member'
fieldsets = ((None, {'classes': ('mbship', ),
'fields': ('user', 'myfield')}), )
class MembershipAdmin(admin.ModelAdmin):
form = MembershipAdminForm
# inlines
inlines = [PrimaryInline, ]
fieldsets = ((None, {'classes': ('mbship',),
'fields': ('name'), }), )
class PrimaryAdmin(admin.ModelAdmin):
form = PrimaryAdminForm
list_display = ('__unicode__', 'user', 'status', 'date_used' )
search_fields = ['user__first_name', 'user__last_name', 'user__email']
fieldsets = ((None, {'classes': ('mbship',),
'fields': ('user', 'membership', 'myfield'), }), )
def clean_myfield(self):
data = self.cleaned_data['myfield']
data += "_extra" # not actual cleaning code
return unicode(data)
Validation occurs on ModelForm objects, not ModelAdmin objects. If you want to override any clean methods then you have to create your own ModelForm descendent for each required model.
In your example, the PrimaryInline class does not specify a form. If not specified, the form used will be a ModelForm, which doesn't have any of your custom clean methods.
Try this:
class PrimaryInline(admin.StackedInline):
# ... existing code ...
form = PrimaryAdminForm
This will now use your custom PrimaryAdminForm with associated clean() methods.
Even though a field is marked as 'editable=False' in the model, I would like the admin page to display it. Currently it hides the field altogether.. How can this be achieved ?
Use Readonly Fields. Like so (for django >= 1.2):
class MyModelAdmin(admin.ModelAdmin):
readonly_fields=('first',)
Update
This solution is useful if you want to keep the field editable in Admin but non-editable everywhere else. If you want to keep the field non-editable throughout then #Till Backhaus' answer is the better option.
Original Answer
One way to do this would be to use a custom ModelForm in admin. This form can override the required field to make it editable. Thereby you retain editable=False everywhere else but Admin. For e.g. (tested with Django 1.2.3)
# models.py
class FooModel(models.Model):
first = models.CharField(max_length = 255, editable = False)
second = models.CharField(max_length = 255)
def __unicode__(self):
return "{0} {1}".format(self.first, self.second)
# admin.py
class CustomFooForm(forms.ModelForm):
first = forms.CharField()
class Meta:
model = FooModel
fields = ('second',)
class FooAdmin(admin.ModelAdmin):
form = CustomFooForm
admin.site.register(FooModel, FooAdmin)
Add the fields you want to display on your admin page.
Then add the fields you want to be read-only.
Your read-only fields must be in fields as well.
class MyModelAdmin(admin.ModelAdmin):
fields = ['title', 'author', 'published_date', 'updated_date', 'created_date']
readonly_fields = ('updated_date', 'created_date')
You could also set the readonly fields as editable=False in the model (django doc reference for editable here). And then in the Admin overriding the get_readonly_fields method.
# models.py
class MyModel(models.Model):
first = models.CharField(max_length=255, editable=False)
# admin.py
class MyModelAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
return [f.name for f in obj._meta.fields if not f.editable]
With the above solution I was able to display hidden fields for several objects but got an exception when trying to add a new object.
So I enhanced it like follows:
class HiddenFieldsAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
try:
return [f.name for f in obj._meta.fields if not f.editable]
except:
# if a new object is to be created the try clause will fail due to missing _meta.fields
return ""
And in the corresponding admin.py file I just had to import the new class and add it whenever registering a new model class
from django.contrib import admin
from .models import Example, HiddenFieldsAdmin
admin.site.register(Example, HiddenFieldsAdmin)
Now I can use it on every class with non-editable fields and so far I saw no unwanted side effects.
You can try this
#admin.register(AgentLinks)
class AgentLinksAdmin(admin.ModelAdmin):
readonly_fields = ('link', )