Django Admin: Show list of models - django

I am using Django 1.8.8.
I have a lot of models defined as such:
class MainModel(models.Model):
value = models.IntegerField(null=False, editable=True, default=20)
dt_modified = models.DateTimeField(null=True, auto_now=True)
class MyModel1(MainModel, models.Model):
name = models.CharField(null=False, editable=False, max_length=50)
class MyModel2(MainModel, models.Model):
foo = models.CharField(null=False, editable=False, max_length=50)
My admin page is currently setup as such,
class MyModelAdmin(admin.ModelAdmin):
list_display = ('value', 'dt_modified')
search_fields = ['value']
date_hierarchy = 'dt_modified'
models = [MyModel1, MyModel2]
admin.site.register(models, MyModelAdmin)
I want to setup my admin page as follows:
Have one link on the top page/admin, "MainModels" which directs me to another page where I can select from a list of all derived models of MyModel, i.e. [MyModel1, MyModel2, ....MyMoneyN] which lets me edit the selected derived model. MyModel#.
The problem with the code above is it creates a top level link for each numbered model.

You're really best off using something like Grapelli or another custom admin replacement. You can set custom pages with different sets of models, adjust hidden / shown by default, etc. Trying to do this with vanilla admin is going to be problematic.
Here is an example of something close to what you want to do with grappelli
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from grappelli.dashboard import modules, Dashboard
class MyDashboard(Dashboard):
def __init__(self, **kwargs):
Dashboard.__init__(self, **kwargs)
# append an app list module for "Applications"
self.children.append(modules.AppList(
title=_('Applications'),
column=1,
css_classes=('grp-collapse grp-closed',),
collapsible=True,
exclude=('django.contrib.*',),
))

You need your own changelist to do this:
class MyChangeList(ChangeList):
def url_for_result(self, result):
link = your_function_to_create_link()
return link
Then use your Changelist in ModelAdmin:
class MyModelAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
return MyChangeList

Related

How to make a field read-only inside a CreateView

I am a Django beginner and I am trying to make read-only a 'price' field for an order. I think, based on what I have understood, this cannot be done inside the model itself, but rather inside a form.
Since I am using a CreateView generic view, I thought this could have been done by setting the attribute disabled equal to True, as said here.
so what I have done is, in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .models import Order
from django import forms
# Create your views here.
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
meal_price = forms.DecimalField(disabled=True)
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
But this doesn't work.
Here is my models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
Can anybody give me a hint?
Please consider that I am still learning so I would prefer coded answers to descriptive ones.
Thank you in advance
Ok, thanks to dirkgroten, I have worked out the answer.
Basically what is needed (in my case) is:
an Order model in models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
an OrderForm(ModelForm) in forms.py that modifies the price field setting the disabled attribute to true
from django.forms import ModelForm
from .models import Order
from django import forms
class OrderForm(ModelForm):
meal_price = forms.DecimalField(max_digits=5, decimal_places=2, disabled=True)
class Meta:
model = Order
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
an OrderView(CreateView) in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .forms import OrderForm
# Create your views here.
class OrderView(CreateView):
form_class = OrderForm
template_name = 'home.html'
I have no experience with Django's CreateView but from what I read it works similar to a separate form. You could try something like this:
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
def __init__(self, *args, **kwargs):
super(CreateOrderView, self).__init__(*args, **kwargs)
self.fields['meal_price'].widget.attrs['disabled'] = True
From my experience, the disabled attribute will be good for security reasons as far as protecting against the user editing the HTML and changing the value. However you won't be able to access this value when passed into a clean method. If you need to perform actions on the value you should change 'disabled' to 'readonly', but you won't have the same data protection that disabled offers.

Complicated "limit_choices_to" function in Django

I have Django database with 2 models: DeviceModel and Device. Let's say, for example, DeviceModel object is "LCD panel" and Device object is "LCD panel №547". So these two tables have ManyToOne relationship.
class DeviceModel(models.Model):
name = models.CharField(max_length=255)
class Device(models.Model):
device_model = models.ForeignKey(DeviceModel)
serial_number = models.CharField(max_length=255)
Now I need to add some relations between DeviceModel objects. For example "LCD Panel" can be in "Tablet" object or in "Monitor" object. Also another object can be individual, so it doesn't link with other objects.
I decided to do this with ManyToMany relationship, opposed to using serialization with JSON or something like that (btw, which approach is better in what situation??).
I filled all relationships between device models and know I need to add relationship functional to Device table.
For that purpose I added "master_dev" foreignkey field pointing to 'self'. It works exactly as I need, but I want to restrict output in django admin panel. It should display only devices, that are connected through device_links. Current code:
class DeviceModel(models.Model):
name = models.CharField(max_length=255)
device_links = models.ManyToManyField('self')
class Device(models.Model):
device_model = models.ForeignKey(DeviceModel)
serial_number = models.CharField(max_length=255)
master_dev = models.ForeignKey('self', blank=True, null=True)
So, how can I limit output of master_dev field in admin panel?
There is a function "limit_choices_to", but I can't get it to work...
in forms.py:
def master_dev_chioses():
chioses = DeviceModel.objects.filter(do your connection filter here - so not all Devicemodels comes to choicefield)
class DeviceForm(forms.ModelForm):
class Meta:
model = Device
def __init__(self, *args, **kwargs):
super(Device, self).__init__(*args, **kwargs)
self.fields['master_dev'].choices = master_dev_chioses()
While there is no direct answer to my question about "limit_choices_to" function, I post solution that achieves desired output:
from django import forms
from django.contrib import admin
from .models import DeviceModel, Device
class DeviceForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DeviceForm, self).__init__(*args, **kwargs)
try:
linked_device_models = self.instance.device_model.device_links.all()
linked_devices = Device.objects.filter(device_model__in=linked_device_models)
required_ids = set(linked_devices.values_list("id", flat=True))
self.fields['master_dev'].queryset = Device.objects.filter(id__in=required_ids).order_by("device_model__name", "serial_number")
except:
# can't restrict masters output if we don't know device yet
# admin should edit master_dev field only after creation
self.fields['master_dev'].queryset = Device.objects.none()
class Meta:
model = Device
fields = ["device_model", "serial_number", "master_dev"]
class DeviceAdmin(admin.ModelAdmin):
form = DeviceForm
list_display = ('id', 'device_model', 'serial_number')
list_display_links = ('id', 'device_model')
search_fields = ('device_model__name', 'serial_number')
list_per_page = 50
list_filter = ('device_model',)

Django-CMS - CMS section of admin disappears when I specify a model

I'm trying to duplicate the example in 2.4 in the Django-CMS docs (http://docs.django-cms.org/en/2.3.5/extending_cms/custom_plugins.html). However, whenever I specify the model in cms_plugins.py under class HelloPlugin, the entire CMS section in the admin disappears. Any idea what's causing this?
models.py
from django.db import models
class MyModel(models.Model):
title = models.CharField(max_length=50, null=True, blank=True)
def __unicode__(self):
return self.title
from cms.models.pluginmodel import CMSPlugin
class HelloPlugin(CMSPlugin):
ad = models.ForeignKey('core.MyModel', related_name='plugins')
def __unicode__(self):
return self.ad.title
cms_plugins.py
class HelloPlugin(CMSPluginBase):
model = MyModel
name = _("MyModel Plugin")
render_template = "myplugin.html"
def render(self, context, instance, placeholder):
context['instance'] = instance
return context
plugin_pool.register_plugin(HelloPlugin)
Small, but significant mistake. I was importing the model, and not the plugin.

django ForeignKey model filter in admin-area?

Hi I need really very very simple example. First my models:
#This my student models
from django.db import models
SEX_CHOICES= (
('M', 'Male'),
('F', 'Female'),
)
class Students(models.Model):
student_name = models.CharField(max_length=50)
student_sex = models.CharField(max_length=8, choices=SEX_CHOICES)
student_city = models.Charfield(max_length=50)
student_bio = models.TextField()
def __unicode__(self):
return self.student_name
O.K. Let see my ClassRooms Model.
#This my ClassRooms models
from django.db import models
from myproject.students.models import *
class ClassRooms(models.Model):
class_number= models.CharField(max_length=50)
class_student_cities = models.ForeignKey(Students)
class_year = models.DateField()
def __unicode__(self):
return self.class_number
How can i show in the class_student_cities area the Students.student_city datas? I guess that about django-admin area. When i do it withclass_student_cities = models.ForeignKey(Students) i just see in that area the Students.student_name data (ex: John Smith). I want to see JUST Students.student_cities data (ex: NewYork). Can you give me a little example?
Should i use something like that:
class_student_cities = models.ForeignKey(Students.student_cities)
Many Thanks!
Try redifinition unicode method.
def __unicode__(self):
return self.student_city
So you'll see in the field student city.
Well, I tried to remake your application to set data with forms class. Something like this in admin.py in your application:
from django.contrib import admin
from django import forms
from myapp.models import *
class ClassRoomsAdminForm(forms.ModelForm):
class Meta:
model = ClassRoom
def __init__(self, *arg, **kwargs):
super(ClassRoomsAdminForm, self).__init__(*arg, **kwargs)
self.fields[' class_student_cities'].choices = [(csc.id,csc.student_city) for csc in Students.objects.all()
class ClassRoomsAdmin(admin.ModelAdmin):
form = ClassRoomsAdminForm
admin.site.register(ClassRooms,ClassRoomsAdmin)
Maybe you'll need to fix something, but I hope it will work. You will set init function to your forms, so in admin panel you set all choices to everything you keep in your Students model. csc.id you'll need to make this object iterable (cities aren't unique) and then you can choose everything from Students model to set in the field.

Django limit_choices_to at circular relation

I've implemented a circular OneToMany relationship at a Django model and tried to use the limit_choices_to option at this very same class.
I can syncdb without any error or warning but the limit is not being respected.
Using shell I'm able to save and at admin I receive the error message:
"Join on field 'type' not permitted.
Did you misspell 'neq' for the lookup
type?"
class AdministrativeArea(models.Model):
type = models.CharField(max_length=1, choices=choices.ADMIN_AREA_TYPES)
name = models.CharField(max_length=60, unique=True)
parent = models.ForeignKey('AdministrativeArea',
null=True,
blank=True,
limit_choices_to = Q(type__neq='p') & Q(type__neq=type)
)
The basic idea for the limit_choices_to option is to guarantee that any type "p" cannot be parent ofr any other AdministrativeArea AND the parent cannot be of the same type as the current AdministrativeArea type.
I'm pretty new to Django ... what am I missing?
Thanks
You can create a model form that adjusts specific field's queryset dynamically when working with existing model instance.
### in my_app/models.py ###
class AdministrativeArea(models.Model):
type = models.CharField(max_length=1, choices=choices.ADMIN_AREA_TYPES)
name = models.CharField(max_length=60, unique=True)
parent = models.ForeignKey('AdministrativeArea',
null=True,
blank=True,
limit_choices_to = Q(type__neq='p')
)
### in my_app/admin.py ###
from django.contrib import admin
import django.forms as forms
from my_app.models import AdministrativeArea
class class AdministrativeAreaAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AdministrativeAreaAdminForm, self).__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
if instance is not None:
parentField = self.fields['parent']
parentField.queryset = parentField.queryset.filter(type__neq=instance.type)
class Meta:
model = AdministrativeArea
class AdministrativeAreaAdmin(admin.ModelAdmin):
form = AdministrativeAreaAdminForm
Such form could be used also outside admin site if you need the filtering there.