How can create model that couldn't be changed by admin? - python

I need to create Django model that couldn't by admin, but he should be avialable to see it.
It's content will be input from site.
How can I do it?

Basically if you want to disable the editability of the model you may want to make use of Django Admin's permission framework, like this:
class PersonAdmin
def has_change_permission(self, request, obj=None):
# only allows superusers to edit
return request.user.is_superuser
You may also want to try the readonly_field like:
class PersonAdmin
readonly_fields = ('name','sex',)
But the problem of doing this is that you will see the save buttons on the editing page despite that nothing is allowed to be changed, which is probably not what you want

Designate that it should be read only
In your Model Admin, you can specify that certain fields are not to be changed.
class ProfileAdmin(admin.ModelAdmin):
readonly_fields = ('source', 'campaign')
Just put that in your admin.py and then when you got to register your site, use this:
admin.site.register(Profile, ProfileAdmin)
instead of what you are probably currently using, which would be
admin.site.register(Profile)

You can list the fields you want on both fields and readonly_fields.
class AuthorAdmin(admin.ModelAdmin):
fields = ('name', 'title')
readonly_fields = ('name', 'title')

Related

Django, admin page

I have a problem with achieving this functionality: An administrator must be able to view data for a specific user group in the administration page, how could I do this in my django project?
Thank you in advance for your answers
There are a few ways to do this.
Assuming a custom usermodel with a custom property called group_admin_name, this would restrict the ability of the admin to see that individual group.
class CustomUserAdmin(UserAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(groups__name=request.user.group_admin_name)
If you want them filter on all groups, but one group at a time, then the admin has the list_filter property.
class CustomUserAdmin(UserAdmin):
list_filter = ('groups__name')
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

How to add an extra field to a Django ModelForm?

So I'm working on an admin page. I'm registering the form with admin.site.register. And I want to add an extra field to the form, which will let me populate a TextField with a file contents.
Therefore I need to add an extra FileInput to upload the file and populate the TextField with its contents. I am trying this:
class PersonForm(forms.ModelForm):
extra_field = forms.FileInput()
class Meta:
model = Person
fields = '__all__'
but the field is not showing. Any ideas?
Also I have no clue where to access the file contents and populate the TextField with that before saving the model.
Thanks in advance.
My problem was in this line:
extra_field = forms.FileInput()
I solved the problem changing the line to:
extra_field = forms.FileField()
Thanks to all willing to help.
Try to do it in the constructor.
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = '__all__'
def __init__(self, *args, **kwargs):
super(PersonForm, self ).__init__(*args, **kwargs)
self.fields['extra_field'] = forms.FileInput()
And since you are using the django admin, you need to change the form in the admin too.
What you've done is ok according to the documentation, read note here - https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#overriding-the-default-fields
To register it in the admin you should add something like this to your admin.py:
from django.contrib import admin
from .forms import PersonForm
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
form = PersonForm
Example from here - https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#admin-custom-validation
EDIT: it is necessary to actually register custom ModelAdmin, there are two equivalent ways: using decorator, as in the example above, or use admin.site.register(Person, PersonAdmin).
Documentation for ModelAdmin registration - https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#the-register-decorator
Registration source code - https://github.com/django/django/blob/master/django/contrib/admin/sites.py#L85
Register the model in admin as
admin.site.register(UserProfile)
where UserProfile is a OnetoOnemodel that extends django's builtin User Model then after every changes in models run
python manage.py makemigrations
python manage.py migrate

Add custom form fields that are not part of the model (Django)

I have a model registered on the admin site. One of its fields is a long string expression. I'd like to add custom form fields to the add/update pages of this model in the admin. Based on the values of these fields I will build the long string expression and save it in the relevant model field.
How can I do this?
I'm building a mathematical or string expression from symbols. The user chooses symbols (these are the custom fields that are not part of the model) and when they click save then I create a string expression representation from the list of symbols and store it in the DB. I don't want the symbols to be part of the model and DB, only the final expression.
Either in your admin.py or in a separate forms.py you can add a ModelForm class and then declare your extra fields inside that as you normally would. I've also given an example of how you might use these values in form.save():
from django import forms
from yourapp.models import YourModel
class YourModelForm(forms.ModelForm):
extra_field = forms.CharField()
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
# ...do something with extra_field here...
return super(YourModelForm, self).save(commit=commit)
class Meta:
model = YourModel
To have the extra fields appearing in the admin just:
Edit your admin.py and set the form property to refer to the form you created above.
Include your new fields in your fields or fieldsets declaration.
Like this:
class YourModelAdmin(admin.ModelAdmin):
form = YourModelForm
fieldsets = (
(None, {
'fields': ('name', 'description', 'extra_field',),
}),
)
UPDATE:
In Django 1.8 you need to add fields = '__all__' to the metaclass of YourModelForm.
It it possible to do in the admin, but there is not a very straightforward way to it. Also, I would like to advice to keep most business logic in your models, so you won't be dependent on the Django Admin.
Maybe it would be easier (and maybe even better) if you have the two seperate fields on your model. Then add a method on your model that combines them.
For example:
class MyModel(models.model):
field1 = models.CharField(max_length=10)
field2 = models.CharField(max_length=10)
def combined_fields(self):
return '{} {}'.format(self.field1, self.field2)
Then in the admin you can add the combined_fields() as a readonly field:
class MyModelAdmin(models.ModelAdmin):
list_display = ('field1', 'field2', 'combined_fields')
readonly_fields = ('combined_fields',)
def combined_fields(self, obj):
return obj.combined_fields()
If you want to store the combined_fields in the database you could also save it when you save the model:
def save(self, *args, **kwargs):
self.field3 = self.combined_fields()
super(MyModel, self).save(*args, **kwargs)
Django 2.1.1
The primary answer got me halfway to answering my question. It did not help me save the result to a field in my actual model. In my case I wanted a textfield that a user could enter data into, then when a save occurred the data would be processed and the result put into a field in the model and saved. While the original answer showed how to get the value from the extra field, it did not show how to save it back to the model at least in Django 2.1.1
This takes the value from an unbound custom field, processes, and saves it into my real description field:
class WidgetForm(forms.ModelForm):
extra_field = forms.CharField(required=False)
def processData(self, input):
# example of error handling
if False:
raise forms.ValidationError('Processing failed!')
return input + " has been processed"
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
# self.description = "my result" note that this does not work
# Get the form instance so I can write to its fields
instance = super(WidgetForm, self).save(commit=commit)
# this writes the processed data to the description field
instance.description = self.processData(extra_field)
if commit:
instance.save()
return instance
class Meta:
model = Widget
fields = "__all__"
You can always create new admin template, and do what you need in your admin_view (override the admin add URL to your admin_view):
url(r'^admin/mymodel/mymodel/add/$','admin_views.add_my_special_model')
If you absolutely only want to store the combined field on the model and not the two seperate fields, you could do something like this:
Create a custom form using the form attribute on your ModelAdmin. ModelAdmin.form
Parse the custom fields in the save_formset method on your ModelAdmin. ModelAdmin.save_model(request, obj, form, change)
I never done something like this so I'm not completely sure how it will work out.
The first (highest score) solution (https://stackoverflow.com/a/23337009/10843740) was accurate, but I have more.
If you declare fields by code, that solution works perfectly, but what if you want to build those dynamically?
In this case, creating fields in the __init__ function for the ModelForm won't work. You will need to pass a custom metaclass and override the declared_fields in the __new__ function!
Here is a sample:
class YourCustomMetaClass(forms.models.ModelFormMetaclass):
"""
For dynamically creating fields in ModelForm to be shown on the admin panel,
you must override the `declared_fields` property of the metaclass.
"""
def __new__(mcs, name, bases, attrs):
new_class = super(NamedTimingMetaClass, mcs).__new__(
mcs, name, bases, attrs)
# Adding fields dynamically.
new_class.declared_fields.update(...)
return new_class
# don't forget to pass the metaclass
class YourModelForm(forms.ModelForm, metaclass=YourCustomMetaClass):
"""
`metaclass=YourCustomMetaClass` is where the magic happens!
"""
# delcare static fields here
class Meta:
model = YourModel
fields = '__all__'
This is what I did to add the custom form field "extra_field" which is not the part of the model "MyModel" as shown below:
# "admin.py"
from django.contrib import admin
from django import forms
from .models import MyModel
class MyModelForm(forms.ModelForm):
extra_field = forms.CharField()
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
# Do something with extra_field here
return super().save(commit=commit)
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
You might get help from my answer at :
my response previous on multicheckchoice custom field
You can also extend multiple forms having different custom fields and then assigning them to your inlines class like stackedinline or tabularinline:
form =
This way you can avoid formset complication where you need to add multiple custom fields from multiple models.
so your modeladmin looks like:
inlines = [form1inline, form2inline,...]
In my previous response to the link here, you will find init and save methods.
init will load when you view the page and save will send it to database.
in these two methods you can do your logic to add strings and then save thereafter view it back in Django admin change_form or change_list depending where you want.
list_display will show your fields on change_list.
Let me know if it helps ...
....
class CohortDetailInline3(admin.StackedInline):
model = CohortDetails
form = DisabilityTypesForm
...
class CohortDetailInline2(admin.StackedInline):
model = CohortDetails
form = StudentRPLForm
...
...
#admin.register(Cohort)
class CohortAdmin(admin.ModelAdmin):
form = CityInlineForm
inlines = [uploadInline, cohortDetailInline1,
CohortDetailInline2, CohortDetailInline3]
list_select_related = True
list_display = ['rto_student_code', 'first_name', 'family_name',]
...

Displaying ForeignKey data in Django admin change/add page

I'm trying to get an attribute of a model to show up in the Django admin change/add page of another model. Here are my models:
class Download(model.Model):
task = models.ForeignKey('Task')
class Task(model.Model):
added_at = models.DateTimeField(...)
Can't switch the foreignkey around, so I can't use Inlines, and of course fields = ('task__added_at',) doesn't work here either.
What's the standard approach to something like this? (or am I stretching the Admin too far?)
I'm already using a custom template, so if that's the answer that can be done. However, I'd prefer to do this at the admin level.
If you don't need to edit it, you can display it as a readonly field:
class DownloadAdmin(admin.ModelAdmin):
readonly_fields = ('task_added_at',)
def task_added_at(self, obj):
return obj.task.added_at

django-admin - how to modify ModelAdmin to create multiple objects at once?

let's assume that I have very basic model
class Message(models.Model):
msg = models.CharField(max_length=30)
this model is registered with admin module:
class MessageAdmin(admin.ModelAdmin):
pass
admin.site.register(Message, MessageAdmin)
Currently when I go into the admin interface, after clicking "Add message" I have only one form where I can enter the msg.
I would like to have multiple forms (formset perhaps) on the "Add page" so I can create multiple messages at once. It's really annoying having to click "Save and add another" every single time.
Ideally I would like to achieve something like InlineModelAdmin but it turns out that you can use it only for the models that are related to the object which is edited.
What would you recommend to use to resolve this problem?
This may not be exactly what you are looking for, but if you want to create multiple objects at the same time you could to somehthing like this:
#In /forms.py
MessageAdminForm(forms.ModelForm):
msg = CharField(max_length=30)
count = IntegerField()
#In /admin.py
from app.admin import MessageAdminForm
MessageAdmin(admin.ModelAdmin):
form = MessageAdminForm
fieldsets = (
(None, {
'fields' : ('msg','count')
}),)
def save_model(self, request, obj, form, change):
obj.msg = form.cleaned_data['msg']
obj.save()
for messages in range(form.cleaned_data['count']):
message = Message(msg=form.cleaned_data['msg'])
message.save()
Basicly what you are doing is creating a custom form for your admin template, which ask the user how many times the object shall be created. The logic is than interpreted in the save_model method.
As a workaround, Since, It is likely that you have a FK to User, so you could define an InlineModel on the User model.
Otherwise, the easiest approach may be to create a custom admin view since, there isn't a generic admin view that displays and saves formsets.
This is easy if you are using an Inline. Then you could use extra = 10 or however many extra formsets you want. There doesn't seem to be an equivalent for the ModelAdmin.
Of course in your messages model you would need to create a ForeignKey to some sort of message grouping model as another layer of function and to get the multi-formset layout that you are looking for.
For example:
models.py:
class Group(models.Model):
name = models.CharField(max_length=30)
class Message(models.Model):
msg = models.CharField(max_length=30)
grp = models.ForeignKey(Group)
admin.py:
class MessageInline(admin.TabularInline):
model = Message
extra = 10
class GroupAdmin(admin.ModelAdmin):
inlines = [MessageInline]
admin.site.register(Group, GroupAdmin)
This would give you what you want in the Admin view and create grouping (even if you only allow for one group) and the only extra field would be the name in the group model. I am not even sure you would need that. Also I am sure the value for extra could be generated dynamically for an arbitrary value.
I hope this helps!

Categories