I want to use the Django admin interface for a very simple web application but I can't get around a problem that should not be that hard to resolve ..
Consider the following:
class Contact(models.Model):
name = models.CharField(max_length=250, blank=False)
created_by = models.ForeignKey(User, blank=False)
I can't find a way to auto-populate the created_by field and make the Django admin aware of it. Most of the method I've seen implies overloading the Object's save method and pass it the request user. They all requires to build your custom views and/or forms.
Optimally the form to create new contacts in the admin site should not show the created_by field (which is quite easy) and auto-populate it with the current user (which seems harder than it should).
http://code.djangoproject.com/wiki/CookBookNewformsAdminAndUser
Involves implementing save methods on your ModelAdmin objects.
You need to specify a default for the field, in this case a method call that gets the current user (see the auth documentation to get the current user).
I really wanted a better solution than the ones I found elsewhere, so I wrote some middleware - implementation on the Populate user Id question. My solution requires no changes to any form or view.
I did it by overriding the save_model method in django admin. Since you mentioned that you wanted to use the Django admin interface. So, I think my solution will be the most appropriate one. The solution goes like this:
First register the Contact model in the admin by excluding the 'created_by' field
#admin.register(Contact)
class Notice(admin.ModelAdmin):
list_display = ('name', 'created_by')
exclude = ('created_by', )
Then override the save_model method in the same class Notice
def save_model(self, request, obj, form, change,):
obj.created_by = request.user
super().save_model(request, obj, form, change)
Related
I have a fairly simple django model in a Wagtail CMS; essentially:
from django.db import models
class Thingy(models.Model):
name = models.CharField(max_length=255, blank=False, null=False, unique=True)
# Many other fields.
panels = [
FieldPanel("name"),
# ...
]
I need to perform an action when this model is saved via the Wagtail model edit form (eg /admin/section/thingy/123/edit).
Currently, I have registered a post_save signal, however this has resulted in the method being called when the model is saved programmatically (via an import sync task).
I've had a look in the Django docs, but can't see anything obvious... is there a way to register a signal for the form submission (ideally after the internal submission and save is handled).
(If it's any relevance; I need to trigger a search reindex based on a relation to the model; anything that references instance 123 of thing Things that was saved needs to be reindexed)
If you are using ModelAdmin, you can customize your edit view and put your reindexing code in there.
im looking for some solution about make a "create-only" field on django admin using models.
I saw some questions before, but no one can answer the core question: the field should appear when the user are creating on admin panel, but i dont want to be able to edit.
models.py
class Fonte(Always):
source_slug = models.CharField(max_length=255)
admin.py
#admin.register(Fonte)
class FonteAdmin(admin.ModelAdmin):
readonly_fields = ['source_slug']
the "readonly_fields" solves the problem in the matter of editing in the future, but ends up forbidding when creating.
Problem: Im using this field to make a hash and i dont wanna this change evermore.. I thought about using a second field that would generate a hash on top of the editable one field in the creation, after that the field would be "dead", but that seems to me to be contrary to the 2 way of normalization.
Is there any more elegant way?
Override get_readonly_fields in your Admin class like that:
def get_readonly_fields(self, request, obj=None):
if obj:
return ['source_slug']
return []
Is it possible to create and delete new charfields or textareas through the Django admin page without harcoding them?
For example, I have a simple model, registered in Django admin page
class DocumentList(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
Obviously, it has only one charfield on admin page, something like:
DocumentList: [___________]
How can I add another one and delete her later if needed from Django admin page without actually hardcoding another charfield/textarea in models.py, to make it look like:
DocumentList: [___________]
*****************[___________]
Django models are not meant to be dynamically altered. You have to explicitly add the fields on your model, run migrations to have the fields created in your database backend, and reload your server process (./manage.py runserver does this automatically).
If you want to create a model that can hold an arbitrary amount of text strings instead of just one or a fixed amount, you need to use a many-to-many relation to another model.
You can use a custom form in the admin, either by using the form option of the get_form method. This is the documentation example for how you'd pass a custom form:
from django import forms
from django.contrib import admin
from myapp.models import Person
class PersonForm(forms.ModelForm):
class Meta:
model = Person
exclude = ['name']
class PersonAdmin(admin.ModelAdmin):
exclude = ['age']
form = PersonForm
You can add extra fields, as in any form.
I was wondering why you wanted this. Since you said in a comment it is to submit information to an API, you can also use an action, taking input from the user in an intermediate page.
EDIT: As became apparent in comments, the form needs to be dynamic for the user, and not when it is created. Therefore, the solution is using inlines, which once created and linked to the current model, allow the user to add any number of related forms to the current form.
I have two user roles in Django:
Commercials
Sellers
I have created two models, Seller Model has a ForeignKey field to Commercials (every seller has a commercial related to). When I register the models in admin I can create Commercials and related sellers using StackedInline, TabularInline etc.
The problem I have is I need to associate users to this models in order to authenticate, login, etc. In admin I need to create a user (in an inline way, not dropdown box)
This is my code:
In models.py:
class Commercial(models.Model):
name = models.CharField(max_length=255, null=True)
user = models.OneToOneField(User, null=True)
class Seller(models.Model):
name = models.CharField(max_length=255, null=True)
commercial = models.ForeignKey('Commercial')
user = models.OneToOneField(User, null=True)
In admin.py:
class SellerAdmin(admin.StackedInline):
model = Seller
extra = 1
class CommercialAdmin(admin.ModelAdmin):
inlines = [SellerAdmin]
admin.site.register(Commercial, CommercialAdmin)
I need to edit, create, users etc. related to this models inline not in a modal window, Is there any way?
There is no way to make a reverse relation (so to say) in the form of Inlines. The Django admin panel doesn't have that ability by default.
What you could do is unregister the default UserAdmin, create a new Admin panel by inheriting the original one, and add this Seller as an Inline. There is still the issue that Django doesn't support multiple inlines, hence, you will not be able to use the Commercial model in the same admin page.
To fix that, you could refer to this information from the doc which shows how to overwrite the automatically created ModelForm for a particular ModelAdmin.
This will however not be that helpful as they are a lot of work. I would rather suggest implementing a workaround in how your application is used, rather than complicating it this much. Depends on the needs of the project, whether you need to go through this much trouble
Django nested inlines library might help you.
I have a model named Domain which looks like this:
class Domain(models.Model):
"""
Model for storing the company domains
"""
user = models.ForeignKey(
User
)
host = models.CharField(
null=False, verbose_name="Host", max_length=128, unique=True
)
I'd like to use Django's generic views for doing CRUD operations on this. There is one field in this model that needs user input but the foreign key field doesn't need any user input. How can I exclude that field from the form that my generic view generates but assign it the value of the current authenticated user.
Thanks.
Have a look at Russel's answer to a similar question on the django-users group earlier this week.
Quoting the answer*:
Forms and Views solve different problems.
The View is solving the problem of "how do I handle this request and
convert it into a response?". The Form is solving the problem of "How
do I convert the POST data in this request into a model object (or a
change to a model object)?".
Very roughly, a view is doing the following:
View gets a request
View works out whether this is a GET or a POST
If its a POST, View asks the Form to turn the Post into a model change
Form returns success or failure
View responds to the success or failure of the Form.
View returns a response.
The functionality of the Form is a complete subset of the
functionality of the View -- and for this reason, it's a completely
interchangable internal component.
Now, in simple situations, it's possible for a View to guess all the
defaults for the form -- all it needs to know is that you're dealing
with a Foo model, and it can construct a default Foo ModelForm.
However, if you have more sophisticated form requirements, you're
going to need a customized Form.
We could have implemented this by exposing all the options of
ModelForm on the View class; but in order to keep everything clean, we
kept the ModelForm isolated, and provided the View with a way to
specify which Form class it's going to use.
So - to cover your use case of excluding fields, you define a
ModelForm that excludes the fields, then let the CreateView know the
form you want to use:
class CampaignForm(forms.ModelForm):
class Meta:
model = Campaign
exclude = ('user', 'name', 'content_inlined')
class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html"
I'm guessing when you say "fix a values for a field", you mean setting
the values of user, name and content_inlined before you save the new
Campaign instance; to do this, you need to inject some extra code into
the form processing logic of the form:
class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html"
def form_valid(self, form):
form.instance.user = ... (something meaningful.. e.g., self.request.user)
return super(CreateCampaignView, self).form_valid(form)
This overrides the default behavior when the form is valid, and sets
the extra values. The super() implementation of form_valid() will then
save the instance.
For the record, this could also be done by overriding the save()
method on the ModelForm -- however, if you do that, you lose the
request object, which you will need if you're trying to set the
instance values to something that is request-sensitive.
*the original answer set self.object.user instead of form.instance.user. This gives an AttributeError so I have changed it above.