My problem is next: I have device table with some params, one of them is device_able, if it is 'enable' I will do something with it. I added two actions to admin actions that change state of device_able on True or False and it works, but when I open any device from table my checkbox is always checked even if it False. I understood that I don't check data from db about state of checkbox but how to do it? I must use template but I do not understand how to connect my template that checks state of checkbox from db to my admin view of Dev app. Could you give me some useful links for exploring? Or I could check state in my admin.py file?
in my models.py
class Dev(models.Model):
#some params for device
device_able = models.BooleanField(default=False, choices=((True, 'enable'), (False, 'disable')))
def __unicode__(self):
return self.device_model
in admin.py
class DevAdminForm(forms.ModelForm):
class Meta:
widgets = {
'device_able': forms.CheckboxInput
}
full code of my admin.py
from django.contrib import admin
from dev.models import Dev
from django import forms
def make_enable(self, request, queryset):
queryset.update(device_able=True)
make_enable.short_description = "Mark selected devices as enable"
def make_disable(self, request, queryset):
queryset.update(device_able=False)
make_disable.short_description = "Mark selected devices as disable"
class DevAdminForm(forms.ModelForm):
class Meta:
widgets = {
'device_able': forms.CheckboxInput
}
class DevAdmin(admin.ModelAdmin):
fields = ['device_model', 'resolution', 'assets_format', 'scale_factor', 'device_able']
list_display = ('device_model', 'resolution', 'assets_format', 'scale_factor', 'device_able')
search_fields = ['device_model']
actions = [make_enable, make_disable]
form = DevAdminForm
class DevInline(admin.SimpleListFilter):
model = Dev
admin.site.register(Dev, DevAdmin)
If you're on the admin view you should use the AdminModel class from the admin module to have your model bound to the form. If you use a ModelForm you must supply the model it is bound to.
That said, in your cse the only need to subclass the AdminModle for your model is to insert tha admin actions, BooleanField are represented by CheckboxInput by default.
I would try in your admin.py:
class DevAdmin(admin.AdminModel):
def make_device_able()
...
actions = [make_device_able]
And register the the class with:
admin.site.register(Dev, DevAdmin)
Hope it helps.
I've found a solution for my problem. First of all, django is so cool that does all work for you. When I created my model I've set 'device_able' as models.CharField. It created in db field with type varchar. After that I've changed in my model 'device_able' to models.BooleanField and changed directly in db type of 'device_able' field on 'bool'. But my checkbox was always checked because only empty string returns False. when I created new project with with the same code, I mean device_able = models.BooleanField, my checkbox start to work correctly!
Related
I have a model with a non-editable field in my models file.
class Table(models.Model):
label = models.CharField(max_length=40, editable=False)
In my admin site, when updating existing Table objects, I can't edit the label. That is fine, this is exactly what I want with this constraint. However, when trying to create an object using the admin site, the field is still hidden, so I can only create Table objects using the shell.
How can I make this field appear only on creation, but on updates, it will be read-only? Thanks.
Try to use readonly_fields in admin.py file
class TableAdmin(admin.ModelAdmin):
readonly_fields = ('label',)
admin.site.register(Table, TableAdmin)
Approach 1
Make label field presented on creation but completely remove it while updating. We will be using ModelAdmin.get_exclude and ModelAdmin.get_fields hooks to accomplish this.
## models.py
class Table(models.Model):
label = models.CharField(max_length=40) # remove editable option
## admin.py
#admin.register(Table)
class TableAdmin(admin.ModelAdmin):
non_editable_fields = ['label']
def get_exclude(self, request, obj=None):
defaults = super().get_exclude(request, obj=obj) or ()
if obj: # if we are updating an object
defaults = (*defaults, *self.non_editable_fields)
return defaults or None
def get_fields(self, request, obj=None):
defaults = super().get_fields(request, obj=obj)
if obj: # if we are updating an object
defaults = tuple(f for f in defaults if f not in self.non_editable_fields)
return defaults
Approach 2
Make label field presented on both creation and update but make it read only while updating. django admin provides a hook for this functioanlity and it is called ModelAdmin.get_readonly_fields. You can find the documentation here.
So we can write the following code to create a field which can be presented/added when creating an object but can not be edited any further despite set value is being displayed(through admin site).
## models.py
class Table(models.Model):
label = models.CharField(max_length=40) # remove editable option
## admin.py
#admin.register(Table)
class TableAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
defaults = super().get_readonly_fields(request, obj=obj)
if obj: # if we are updating an object
defaults = tuple(defaults) + ('label', ) # make sure defaults is a tuple
return defaults
Bonus for Approach 2
Also if you have multiple fields on that table you can use fields property to set the ordering(read only fields which are not specifically ordered will be shown at the end of the field list). Down side for this ordering approach is that you have to remember to reflect model changes to fields property every time you make a change in your model.
I have small issue: simple class
class AModel(db.Model):
id = db.Column(....)
title = db.Column(....)
uniq_text_id = db.Column(db.String(50), unique=True. nullable=False)
def __init__(self):
uniq_text_id = uuid4().hex
Now i adjusted flask-admin package and what administrator be able to create AModel instances. But there is 1 case: field uniq_text_id must be created automatically.
So, right now I cannot create mode because flask-admin says that fiel uniq_text_id in required, but also does not shows prepopulated value.
Is there any way to use prepopulated value in flask-admin forms or avoid somehow this problem without dropping nullable=False constraint?
UPD: #codegeek provided good solution for auto generated fields which still shown on the form. My own solution uses provided by flask-admin functionality allows to explicitly declares which columns are shown and which - hidden.
You need to override the ModelView class for your model. Something like:
class AModelAdmin(sqlamodel.ModelView):
uuidtext = uuid4()
form_args = dict(
uniq_text_id=dict(default=uuidtext)
)
def __init__(self, session):
super(AModelAdmin, self).__init__(AModel, session)
Then you just need to add this to your admin
admin.add_view(AModelAdmin(db.session))
It was my fault - read documentation too briefly. Created my model view like fillowing:
class MyUserLessonModelView(MyModelView):
form_excluded_columns = ('uniq_text_id', 'created_by', 'created_date', 'modified_by', 'modified_date')
And all autogenerated fields are disappeared from form.
I am using a Manager on a model based on a Boolean field to filter the objects displayed on the site while showing all objects in the admin unfiltered. The idea is that user's are submitting Locations but I do not want them to show on the site until they have been verified as a valid location based on my criteria.
models.py
class LocationManager(models.GeoManager):
def get_query_set(self):
return super(LocationManager, self).get_query_set().filter(verified=True)
class Location(models.Model):
verified = models.BooleanField(default=False)
objects = LocationManager()
admin_objects = models.Manager()
admin.py
class LocationAdmin(admin.OSMGeoAdmin):
def queryset(self, request):
qs = self.model.admin_objects.get_query_set()
return qs
admin.site.register(Location, LocationAdmin)
In the admin, when I go into a record and check the verified Boolean to True and press save, I get an IntegrityError:
duplicate key value violates unique constraint "localshare_location_pkey"
This worked on another project when default was True and I filtered for False. I am using Postgres. Does anyone know why this is not working or have suggestions for a better way to achieve this?
For anyone interested, this is the answer provided by the django IRC channel. The admin looks for the first Manager by default. All I had to do was flip the order they showed up in the Model. Even with the admin.py overriding queryset and pointing to the other Manager, the order is important.
fixed models.py
class Location(models.Model):
verified = models.BooleanField(default=False)
admin_objects = models.Manager()
objects = LocationManager()
I have a model with a boolean value like that:
class TagCat(models.Model):
by_admin = models.BooleanField(default=True)
This appears as a checkbox in admin.
How could I use this as a radio button in admin?
Also, how do I make it be always with a certain selected value in admin?
Also, I want the default value to be the opposite, when a non-admin user adds a TagCat. This field should be hidden from him.
Can someone tell me how to do this? Django documentation doesn't seem to go in such details.
UPDATE 1: Code that gets me done with 1) (don't forget tot pass CHOICES to the BooleanField in the model)
from main.models import TagCat
from django.contrib import admin
from django import forms
class MyTagCatAdminForm(forms.ModelForm):
class Meta:
model = TagCat
widgets = {
'by_admin': forms.RadioSelect
}
fields = '__all__' # required for Django 3.x
class TagCatAdmin(admin.ModelAdmin):
form = MyTagCatAdminForm
admin.site.register(TagCat, TagCatAdmin)
The radio buttons appear ugly and displaced, but at least, they work
I solved with following info in MyModel.py:
BYADMIN_CHOICES = (
(1, "Yes"),
(0, "No"),
)
class TagCat(models.Model):
by_admin = models.BooleanField(choices=BYADMIN_CHOICES,default=1)
There is another way to do this that is, IMO much easier if you want every field of the same type to have the same widget. This is done by specifying a formfield_overrides to the ModelAdmin. For example:
from django.forms.widgets import Textarea
class MyModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.TextField: {'widget': Textarea},
}
More in the docs: https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_overrides
Here is a more dynamic extension of mgPePe's response:
class MyAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyAdminForm, self).__init__(*args, **kwargs)
self.fields['by_admin'].label = 'My new label'
self.fields['by_admin'].widget = forms.RadioSelect()
class Meta:
model = TagCat
class MyAdmin(admin.ModelAdmin):
fields = ['name', 'by_admin']
form = MyAdminForm
This way you get full control over the fields.
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', )