Django admin change field from text to combobox - django

In my models i have this class:
class temp_main(models.Model):
descr = models.CharField(max_length=200, verbose_name="Description")
notes = models.TextField(null=True, blank=True, verbose_name="Note")
dt = models.DateTimeField(auto_now=True, verbose_name="Created")
owner = models.ForeignKey('auth.User', related_name='tmain_owner', on_delete=models.CASCADE, verbose_name="API Owner")
class Meta:
verbose_name = '1-Main Template'
verbose_name_plural = '1-Main Templates'
def __str__(self):
return self.descr
Then in my admin.py
class temp_mainAdmin(admin.ModelAdmin):
#list_filter = ('main_id__descr', 'l_type')
list_display = ('descr', 'notes', 'dt')
#ordering = ('-l_type',)
def save_model(self, request, obj, form, change):
obj.user = request.user
super(temp_mainAdmin, self).save_model(request, obj, form, change)
def changeform_view(self, request, obj_id, form_url, extra_context=None):
l_mod = temp_main.objects.latest('id')
extra_context = {
'lmod': l_mod,
}
return super(temp_mainAdmin, self).changeform_view(request, obj_id, form_url, extra_context=extra_context)
all done but i would in add and edit form that my field descr (charfield) vas not displayed as a textbox but instead like a combobox with pre-defined key/val data like 1:'Descr1',2:Descr2' etc etc
How can i achieve this result in django admin add and edit form?
thanks in advance

You need to override the ModelAdmin.get_form() method, which will allow you to change the type of input field that Django uses by default for your descr field. Here's what it should look like:
from django.forms import SelectMultiple
class temp_mainAdmin(admin.ModelAdmin):
# No changes to the code you provided above / this is all the same:
#list_filter = ('main_id__descr', 'l_type')
list_display = ('descr', 'notes', 'dt')
#ordering = ('-l_type',)
def save_model(self, request, obj, form, change):
obj.user = request.user
super(temp_mainAdmin, self).save_model(request, obj, form, change)
def changeform_view(self, request, obj_id, form_url, extra_context=None):
l_mod = temp_main.objects.latest('id')
extra_context = {
'lmod': l_mod,
}
return super(temp_mainAdmin, self).changeform_view(request, obj_id, form_url, extra_context=extra_context)
# This is new - override the parent class's get_form method:
def get_form(self, request, obj=None, **kwargs):
# 1. Get the form from the parent class:
form = super(temp_mainAdmin, self).get_form(request, obj, **kwargs)
# 2. Change the widget:
form.base_fields['descr'].widget = SelectMultiple(choices=(
(1, 'Descr1'),
(2, 'Descr2'),
))
# 3. Return the form!
return form

Related

Return inital value in admin change/add form field

I need return only current user on add/change admin model field:
In my admin/model the field fk user, return a list of users, independent if the current user is_admin or not:
How put initial value only current user, and nothing more.
admin.py
class AdminProductModel(admin.ModelAdmin):
inlines = [AdminProductImages,AdminProductFeatures]
model = Product
def formfield_for_dbfield(self, db_field, request, **kwargs):
formfield = super(AdminProductModel, self).formfield_for_dbfield(
db_field, request, **kwargs)
if db_field.name == 'user':
formfield.widget.can_add_related = False
formfield.widget.can_change_related = False
return formfield
def product_thumb(self, obj):
# return HTML link that will not be escaped
return mark_safe(
'<img src="%s" style="width:30px;height:30px;">' % (ProductImages.objects.filter(product=obj).first().image_file_w200_png.url)
)
product_thumb.short_description = 'Preview'
list_display = ['product_thumb','user','slug','created_at']
list_display_links = ('slug','product_thumb')
exclude = ['slug','product_title_seo']
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(user_id=request.user)
models.py
class Product(models.Model):
user = models.ForeignKey(User,verbose_name = u'usuário', on_delete=models.CASCADE)
#...otherfields

How can i auto fill my field and make it non editable in django admin site

I need to autopopulate a field in admin site and make foreignkey field non editable with current_user :
views.py:
def my_view(request):
obj = model.objects.first()
response = HttpResponse(file, content_type='
application/vnd.ms-excel',
)
return response
urls.py:
path('temo/fill',views.my_view,name = 'my-view')
models.py
class Model(BaseModel, SingletonModel):
file = models.FileField(
upload_to='',
validators=[FileExtensionValidator([''])]
)
person_uploaded = models.ForeignKey(
'somemodel',
related_name='s',
null=True,
on_delete=models.SET_NULL,
)
admin.py:
#admin.register(tTemplate)
class TemplateAdmin(admin.ModelAdmin):
list_display = ('file','person_uploaded',)
readonly_fields = ('person_uploaded',)
def save(self, request):
if not self.id:
self.person_uploaded = request.user
super().save()
Well to prepopulate a field in django admin its straight forward doing something like so:
#admin.register(tTemplate)
class TemplateAdmin(admin.ModelAdmin):
list_display = ('file','person_uploaded',)
readonly_fields = ('person_uploaded',)
def save_model(self, request, obj, form, change):
obj.user = request.user
super().save_model(request, obj, form, change)

modelForm instance not coming through

I'm passing an instance of a model into a modelForm, but, within the view, when I print the form, the values within the model don't show up. Also, when the form is rendered on my template, the values from the instance don't show up.
Views.py
def support_ticket_view(request, id=None):
id = int(id)
instance = SupportTicket.objects.get(id=id, user=user)
form = SupportTicketEditForm(request.POST or None, request.FILES or None, instance=instance)
context = {
'form': form,
}
return render(request, 'accounts/support_ticket_view.html', context)
forms.py
class SupportTicketEditForm (forms.ModelForm):
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop('instance',None)
super(SupportTicketEditForm, self).__init__(*args, **kwargs)
class Meta:
model = SupportTicket
fields = (
'image',
'body_question',
'urgency',
'question_type',
'status',
)
widgets = {
'image': ImageThumbnailFileInput
}
models.py
class SupportTicket(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
question = models.TextField(max_length=10000, null=True, blank=False)
def __str__(self):
return self.user.username
Removed the following:
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop('instance',None)
super(SupportTicketEditForm, self).__init__(*args, **kwargs)
Thanks Daniel Roseman!

request not defined inside forms.py class (forms.Form)

The error
NameError: name 'request' is not defined
My forms.py
class PersonForm(forms.Form):
name = forms.CharField(required=False)
job_title = forms.CharField(required=False)
status = forms.TypedChoiceField(choices=Person.STATUS_CHOICES)
project = Project.objects.get(users=request.user, pk=self.kwargs.get('pk'))
company = forms.ModelChoiceField(queryset=project.companies.all(),required=False)
new_company = forms.CharField(required=False)
note = forms.CharField(required=False)
def __init__(self, *args, **kwargs):
super(PersonForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
def clean(self):
return self.cleaned_data
views.py
class PersonCreate(LoginRequiredMixin, FormView):
template_name = 'person/person_form.html'
form_class = PersonForm
success_url = '/project/'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
cleaned_data = form.clean()
active_project = self.request.session['active_project']
project = Project.objects.get(users=self.request.user, pk=self.request.session['active_project'])
if cleaned_data['name']:
person, created = Person.objects.get_or_create(
name=cleaned_data['name'],
job_title=cleaned_data['job_title'],
created_by=self.request.user,
status=cleaned_data['status'],
project=project
)
if cleaned_data['new_company']:
company, created = Company.objects.get_or_create(name=cleaned_data['new_company'], project=project, created_by=self.request.user)
company.persons.add(person)
company.save()
if cleaned_data['note']:
person.note_set.create(content=cleaned_data['note'], created_by=self.request.user)
person.save()
if cleaned_data['company']:
company = project.companies.get(name=cleaned_data['company'])
company.persons.add(person)
company.save()
return super().form_valid(form)
def get_success_url(self):
return self.request.POST.get('next', '/project/' + str(self.request.session['active_project']))
I want to filter the queryset on the forms.ModelChoiceField field company. Based on the companies of the project the user has access to. How would I do that? Can I access request.session data as well here?
You can't do that like this, because Django forms don't have access to the request at all.
So the best approach I can think of is to pass the user to the form and then use the data when initialized.
First you have to pass the user and pk in the view.
views.py:
# ...
form = PersonForm(user=request.user, pk=kwargs.get('pk'))
Then in your form, you can catch both kwargs and update the project with the correct value,
class PersonForm(forms.Form):
# your form fields code ...
def __init__(self, *args, **kwargs):
# get the user and pk
user = kwargs.pop('user', None)
pk = kwargs.pop('pk', None)
# update project field
super(PersonForm, self).__init__(*args, **kwargs)
self.fields['project'] = Project.objects.get(users=user, pk=pk)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
In class-based views to update Form kwargs:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({"request": self.request})
return kwargs

Django unique together validation with request user

In Django, I need to validate unique_together the author and title fields.
The problem is that the author is the request.user so what is the best approach to validate the admin form?
I have this admin:
#admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
exclude = ('author',)
def save_model(self, request, obj, form, change):
"""Save ``author`` as request user."""
if getattr(obj, 'author', None) is None:
obj.author = request.user
super().save_model(request, obj, form, change)
I can query inside the save_model() and filter both author and title but that doesn't really work well.
I also tried with a forms.ModelForm but I can't manage to get the request.user inside the clean() method.
This is my model:
class Document(models.Model):
title = models.CharField(max_length=32, blank=True)
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
class Meta:
unique_together = (('title', 'author'),)
Thank you.
Put a unique_together constrain on your model
class Foo(models.Model):
field_1 = models.CharField(max_length=50)
field_2 = models.CharField(max_length=50)
class Meta:
unique_together = ('field_1', 'field_2')
Django will automatically do the validation for you, if it fails it will throw an IntegrityError (from django.db import IntegrityError)
The author field cannot be excluded from the admin. The solution is to hide the author using the get_form method. The save_model method is still useful if someone tries to change the hidden input value.
#admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
# exclude = ('author',)
def get_readonly_fields(self, request, obj=None):
"""Make ``author`` field readonly on update."""
return ['author'] if obj else []
def get_form(self, request, obj=None, **kwargs):
"""Hide ``author`` selection default to request user on create."""
form = super().get_form(request, obj, **kwargs)
if not obj:
form.base_fields['author'].initial = request.user
form.base_fields['author'].widget = forms.HiddenInput()
return form
def save_model(self, request, obj, form, change):
"""Save ``author`` field as request user on create."""
if getattr(obj, 'author') != request.user and not change:
obj.author = request.user
super().save_model(request, obj, form, change)