A way to show/hide a field on Django - python

I have this as my list display in Django admin
list_display = ('product', 'price', 'purchase_date', 'confirmed', 'get_po_number', 'notes')
in models.py:
class PurchaseOrder(models.Model):
notes = models.TextField( null=True, blank= True)
This is what it looks like here:
[1]: http://i.imgur.com/ZyKmpoF.png '
As you can see 'notes' could take up a lot of room, so is there a way I can view/hide that field with the click of a button?

Instead of doing a button, you can resize the field to become smaller.
class PurchaseAdmin(admin.ModelAdmin):
formfield_overrides = {
models.CharField: {'widget': TextInput(attrs={'size':'20'})},
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':40})},
}
admin.site.register(PurchaseOrder, PurchaseAdmin)
If you really want to have another button, you can use your custom Inline class to define the fields:
class CustomInline(admin.TabularInline):
readonly_fields = [...'link',...]
# important part which define what "link" looks like
def link(self, instance):
url = # your link to display the note
return mark_safe(u'View Note".format(u=url))
And in your custom admin class, use this Inline class instead:
class PurchaseAdmin(admin.ModelAdmin):
inlines = [CustomInline]

Related

How to set fields/model to readonly if one attribute is set to true in Django?

So, I got three classes:
class User(...):
#... other fields
name = models.CharField()
class Equipment(...):
#... other fields
inUse = models.Boolean(default=False)
class Ticket(...):
#... Other fields
user = models.ForeignKey(...)
equipment = models.ForeignKey(...)
ended = models.Boolean(default=False)
Now, when I create a new Ticket, the equipment "inUse" attribute changes to True, so I cannot give the same Equipment to more than one User. The thing is that when I set the Ticket to "ended" I'll still be able to change it to not "ended". I want not to be able to change that once I set the "ended" attribute of the Ticket to True.
PS: I'm overriding the method save to make changes in the Equipment when setting up a new Ticket or changing it to "ended".
admin.py
from django.contrib import admin
from .models import Equipamento, TipoEquipamento, Funcionario, Setor, Ticket
# Register your models here.
class EquipamentoAdmin(admin.ModelAdmin):
list_display = ['codigo', 'categoria', 'descricao', 'ativo', 'emUso']
search_fields = ['codigo', 'descricao']
class FuncionarioAdmin(admin.ModelAdmin):
list_display = ['nome', 'setor', 'email']
search_fields = ['codigo', 'nome']
class TicketAdmin(admin.ModelAdmin):
list_display = ['id', 'get_user_name', 'get_equipment_name', 'dataDevolucao', 'finalizado']
autocomplete_fields = ['usuario', 'equipamento']
def get_user_name(self, obj):
return obj.usuario.nome
def get_equipment_name(self, obj):
return obj.equipamento.descricao
get_user_name.short_description = 'Funcionario'
get_equipment_name.short_description = 'Equipamento'
dumb question, after a few minutes I realized I could set a field like "can
Change" in the model so that when I change Status the field will be set to false, and I will not be able to change that model anymore. Daaah, thanks anyways.

Django-Filter: Creating checkboxes for Boolean Field

Models.py
class Task(models.Model):
online = models.BooleanField(blank=False)
I would like to use django-filter to create the following checkboxes in the form:
[] Online
[] Physical
If form is empty or both are ticked, get Task.objects.all(). If only one is ticked, then do Task.objects.filter('Online'=True/False).
I tried to add the following:
import django_filters
from app.models import Task
from django.db import models
class TaskFilter(django_filters.FilterSet):
online = django_filters.BooleanFilter(name='online', lookup_expr='isnull')
class Meta:
model = Task
fields = ['online']
filter_overrides = {
models.BooleanField: {
'filter_class': django_filters.BooleanFilter,
'extra': lambda f: {
'widget': forms.CheckboxInput,
},
},
}
I tried the following widgets: Select, RadioSelect, CheckboxInput, but it seems I get the same output for all of them. I get a dropdown list e.g.
Online: [ Dropdown to select a value from Yes, No, Unknown ]
You can use choices for that kind of stuff:
TYPE_CHOICES = (
(0, 'Online'),
(1, 'Physical'),
)
class Task(models.Model):
type = models.CharField(
choices=TYPE_CHOICES, default=0, max_length=100
)
objects = TaskQuerySet().as_manager()
Then you can filter it as usual:
Task.objects.filter(type=0).filter(...)
And to make it more easier you may add a custom queryset class:
class TaskQuerySet(models.QuerySet):
def get_online(self):
return self.filter(type=0)
def get_physical(self):
return self.filter(type=1)
That will allow you to do things like:
Task.objects.get_online.filter(...)
Task.objects.get_physical.filter(...)
In the filters.py add:
type = django_filters.MultipleChoiceFilter(field_name='type', choices=CHOICES, widget=forms.CheckboxSelectMultiple)
Filters accept a widget argument, so you if you're manually instantiating filters, you can use:
class TaskFilter(django_filters.FilterSet):
online = django_filters.filters.BooleanFilter(widget=forms.CheckboxInput)
class Meta:
model = Task
fields = ['online']
However, if you're using the meta class to declare fields, then you can override the behavior with filter_overrides
class TaskFilter(django_filters.FilterSet):
filter_overrides = {
models.BooleanField: {
'filter_class': filters.BooleanFilter,
'extra': lambda f: {
widget: forms.CheckboxInput
},
}
}
class Meta:
model = MyModel
fields = ['online']
After a lot of testing though, I came to a conclusion that is usually helpful for default=False model fields.
refer: django-filter issue

Django multiple forms on one page with inherited models

For my current Django project I have built a page that uses 3 ModelForms.
The entire page is supposed to let hotels register on the site.
So the first ModelForm is from the Hotel model
Next I want the user to enter 2 forms for images, a main image and a background image. These 2 models are inherited from a basic Image class.
So in this case both images will use the title field, coming from the base class.
Now When I enter a new hotel into the form and also add the 2 images, both images get the same title.
When I take a look at the html code this does makes sense because the input fields both have the same name="title".
For the rest the form works just as expected, it is just this one issue.
Of course I could just take the title field and put them into the child classes as child_class_1_title and child_class_2_title, but that would just break the entire OOP and DRY principle.
How can I make sure that each form pushes the right data into the database?
Thanks a lot in advance.
This is my code for the forms:
class HotelForm(ModelForm):
class Meta:
model = Hotel
fields = ('name', 'address', 'zip_code', 'city', 'website', 'email', 'phone', 'description', 'short_description', 'number_of_rooms', 'facilities',
'activities', 'environment', 'hotel_type')
widgets = {
'facilities': CheckboxSelectMultiple()
}
class HotelGeneralImageForm(ModelForm):
class Meta:
model = HotelGeneralImage
fields = ('title', 'hotel_general_image')
widgets = {
'hotel_general_image': FileWidget()
}
class HotelBackgroundImageForm(ModelForm):
class Meta:
model = HotelBackgroundImage
fields = ('title', 'hotel_background_image')
widgets = {
'hotel_background_image': FileWidget()
}
And the view:
#csrf_exempt
def hotel_registration(request):
if request.method == 'POST':
hotel_form = HotelForm(request.POST, instance=Hotel())
hotel_general_image_form = HotelGeneralImageForm(request.POST, instance=HotelGeneralImage())
hotel_background_image_form = HotelBackgroundImageForm(request.POST, instance=HotelBackgroundImage())
if hotel_form.is_valid() and hotel_general_image_form.is_valid() and hotel_background_image_form.is_valid():
new_hotel = hotel_form.save()
new_hotel_general_image = hotel_general_image_form.save(commit=False)
new_hotel_general_image.hotel = new_hotel
new_hotel_general_image.save()
new_hotel_background_image = hotel_background_image_form.save(commit=False)
new_hotel_background_image.hotel = new_hotel
new_hotel_background_image.save()
return HttpResponseRedirect('registered')
else:
hotel_form = HotelForm(instance=Hotel())
hotel_general_image_form = HotelGeneralImageForm(instance=HotelGeneralImage())
hotel_background_image_form = HotelBackgroundImageForm(instance=HotelBackgroundImage())
context = {'hotel_form': hotel_form,
'hotel_general_image_form': hotel_general_image_form,
'hotel_background_image_form': hotel_background_image_form
}
context.update(csrf(request))
return render_to_response('hotel/hotel-registration-form.html', context)
Form prefix will solve your issue:
hotel_general_image_form = HotelGeneralImageForm(prefix='general', ...)
hotel_background_image_form = HotelBackgroundImageForm(prefix='background', ...)
This way each form will have its own prefix hence will not interfere with other forms.
More in docs - https://docs.djangoproject.com/en/1.9/ref/forms/api/#django.forms.Form.prefix

Django - Add css class to input field in admin

I have the following model:
class InfoBox(models.Model):
type = models.ForeignKey(InfoBoxType)
content = models.TextField()
product = models.ForeignKey(Product)
I want to add a css class to my foreignkey. I've tried the following:
forms.py
class InfoBoxForm(ModelForm):
class Meta:
model = InfoBox
widgets = {
'type': ChoiceField(attrs={'class': 'hej'}),
}
I've also tried this but with the same result...
class InfoBoxForm(forms.Form):
type = forms.ChoiceField(
widget=forms.ChoiceField(attrs={'class':'special'}))
admin.py
class InfoBoxInline(admin.StackedInline):
model = InfoBox
extra = 0
form = InfoBoxForm
But im only getting this:
__init__() got an unexpected keyword argument 'attrs'
Im thinking that this would be pretty easy to do, so what am I doing wrong...?
Try:
widgets = {
'type': Select(attrs={'class': 'hej'}),
}
This is the correct widget for your field.
If you don't want to overwrite the widget, you can use:
self.fields['type'].widget.attrs['class'] = 'hej'
in the form's init method

Overriding list_display in Django admin with custom verbose name

I have overridden the list_display to show inline fields like this:
class opportunityAdmin(admin.ModelAdmin):
list_display = ('name', 'Contact', 'Phone', 'Address', 'discovery_date', 'status' , 'outcome')
search_fields = ['name', 'tags' , 'description']
#readonly_fields = ('discovery_date','close_date')
inlines = [account_contactInline, account_updateInline]
def Contact(self, obj):
return '<br/>'.join(c.account_poc for c in account_contact.objects.filter(opportunity=obj.id).order_by('id')[:1])
def Phone(self, obj):
return '<br/>'.join(c.account_phone for c in account_contact.objects.filter(opportunity=obj.id).order_by('id')[:1])
def Address(self, obj):
return '<br/>'.join(c.account_address for c in account_contact.objects.filter(opportunity=obj.id).order_by('id')[:1])
My question is, in Django admin, the display name of the inline fields header used the function name: Contact, Phone and Address respectively. Actually i wanna display those field header with custom text. I even wanna use Chinese to display them. What have i missed?
You would need to define the short_description attribute on your functions: https://docs.djangoproject.com/en/stable/ref/contrib/admin/actions/#writing-action-functions
For example:
Contact.short_description = 'foo'

Categories

Resources