Django ModelAdmin extra input field not from Model - python

I'm trying to add an extra input to a admin.ModelAdmin for a model I have so I can record some optional text when another input has changed.
I can't get the custom ModelForm recognised as name 'EquipmentAdmin' is not defined. I've tried several different ways of importing but think I must have missed something obvious. It feels like there's a circular reference between the EquipmentAdmin and EquipmentAdminForm as they both include a reference to each other in the code.
I have created my Django application Flightdeck and have these all in the same folder;
models.py
from django.db import models
class Location(models.Model):
name = models.CharField(max_length=45)
class Equipment(models.Model):
unit_id = models.CharField(max_length=45)
located = models.ForeignKey(Location)
located_from = models.DateField()
class EquipmentLocationHistory(models.Model):
unit = models.ForeignKey(Equipment)
located = models.ForeignKey(Location)
start = models.DateField()
end = models.DateField()
change_reason = models.CharField(max_length=45)
admin.py
from django.contrib import admin
from flightdeck.models import *
from flightdeck.forms import EquipmentAdminForm
class EquipmentAdmin(admin.ModelAdmin):
form = EquipmentAdminForm
def save_model(self, request, obj, form, change):
if 'located' in form.changed_data:
try:
old = Equipment.objects.get(unit_id=obj.unit_id)
except Equipment.DoesNotExist:
old = None
if old:
if 'located' in form.changed_data:
located_history = EquipmentLocationHistory(unit=obj, located=old.located, start=old.located_from, end=obj.located_from)
located_history.save()
obj.save()
forms.py
from django import forms
from django.contrib import admin
class EquipmentAdminForm(forms.ModelForm):
reason = forms.CharField()
class Meta:
model = EquipmentAdmin
I would like to include the reason value when I add the EquipmentLocationHistory but can't test what I have as the EquipmentAdminForm isn't loaded.

EquipmentAdmin is not a model. Your ModelForm needs to reference Equipment
from django import forms
from django.contrib import admin
from flightdeck.models import Equipment
class EquipmentAdminForm(forms.ModelForm):
reason = forms.CharField()
class Meta:
model = Equipment
PS: when you have circular references, there are many ways around the problem. The best way with model imports and django is to use django.db.models.get_model('app', 'model')

Related

How to generate a choices tuple for django models field to use in a form

Trying to create a choice field based on another model
I want my choices to be mapped like username: first_name + last_name
When I try username: last_name it does work
I tried doing something like this(Note, I am adding on user_choices and choices=user_choices. The model already existed before me making these changes.)
This works:
he_user_choices = tuple(User.objects.values_list('username', 'last_name'))
Here's what my models.py looks like:
from django.contrib.auth.models import User
owner_choices = tuple(User.objects.values_list('username', 'first_name' + 'last_name'))
class ErrorEvent(models.Model):
"""Error Event Submissions"""
event_id = models.BigAutoField(primary_key=True)
owner = models.IntegerField(verbose_name="Owner", blank=True, choices=owner_choices)
and here's my forms.py
from django import forms
from .models import ErrorEvent
class ErrorEventForm(forms.ModelForm):
class Meta:
model = ErrorEvent
# fields =
exclude = ['event_id']
widgets = {
'owner': forms.Select(),
}
Currently the owner_choices doesn't work, I get an error that says:
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
Any recommendations on what else I can try, or how would I go about fixing my problem?
Thank you in advance!
Please do not work with an IntegerField to refer to an object. A ForeignKey will check referential integrity, and furthermore it makes the Django ORM more expressive.
You thus can implement this with:
from django.conf import settings
class ErrorEvent(models.Model):
event_id = models.BigAutoField(primary_key=True)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name='Owner',
blank=True,
null=True
)
This will work with a ModelChoiceField [Django-doc] that automatically will query the database to render options. This also means that if you add a new User, creating a new ErrorEvent can be linked to that user, since it each time requests the Users from the database.
You can subclass the ModelChoiceField to specify how to display the options, for example:
from django.forms import ModelChoiceField
class MyUserModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return f'{obj.username} ({obj.firstname} {obj.lastname})'
Then we can use this in the form:
class ErrorEventForm(forms.ModelForm):
owner = MyUserModelChoiceField(queryset=User.objects.all())
class Meta:
model = ErrorEvent
# fields =
exclude = ['event_id']
widgets = {
'owner': forms.Select(),
}

How to register inherited sub class in admin.py file in django?

Project Name : fusion
App Name : admin_lte
Python 3.7
Django 2
MySql
Question is "I want to register sub model in django admin-panel",when i write code for model registration in admin.py file that time occurred below error.
django.core.exceptions.ImproperlyConfigured: The model Device is abstract, so it cannot be registered with admin.
NOTE : I used multiple separated model file.
device.py (Model File)
from django.db import models
class Device(models.Model):
device_type = models.CharField(max_length=100,blank=False)
price = models.IntegerField()
status = models.CharField(max_length=10, default="SOLD")
issues = models.CharField(max_length=100, default="No Issues")
class Meta:
abstract = True
def __str__(self):
return 'Device_type:{0} Price:{1}'.format(self.device_type,self.price)
#Inheritance Concept
class Laptop(Device):
pass
class Meta:
db_table = "laptop"
class Desktop(Device):
pass
class Meta:
db_table = "Desktop"
class Mobile(Device):
pass
class Meta:
db_table = "Mobile"
__init__.py File
from django_adminlte.models.employee import Employee
from django_adminlte.models.device import Device
admin.py
from django.contrib import admin
from.models import Employee
from.models import Device
admin.site.register (Employee)
admin.site.register (Device)
I want to show sub model (Desktop,Laptop,Mobile) in admin panel so admin can add some data from admin panel.
Project Structure Image :
I can see in your code Device is a abstract model. So, we should not register it because abstract models do not have associated tables in databases.
from django.contrib import admin
from .models import Employee, Laptop, Mobile, Desktop
admin.site.register(Employee)
admin.site.register(Laptop)
admin.site.register(Mobile)
admin.site.register(Desktop)
I also got a problem when trying to generate an admin CRUD for a class inheriting from an abstract class. But the cause was different so I'll leave my case here in case it helps someone else.
In my case, the problem was that I forgot to make the abstract class inherit from django's models.Model.
Example Code:
time.py
from django.db import models
from applications.utils import UniqueNameMixin
class Month(UniqueNameMixin):
starting_date = models.DateField()
ending_date = models.DateField()
class TimeSensible(models.Model): # Here '(models.Model)' was missing.
class Meta:
abstract = True
month = models.ForeignKey(Month, models.PROTECT)
transaction.py
from django.db import models
from applications.core.models.cash_flow import Concept
from applications.core.models.financial_position import Account
from applications.core.models.time import TimeSensible
class Transaction(models.Model, TimeSensible):
concept = models.ForeignKey(Concept, models.PROTECT, blank=True, null=True)
amount = models.DecimalField(max_digits=10, decimal_places=2)
account = models.ForeignKey(Account, models.PROTECT)
detail = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return '{} - {} - {} - {}'.format(self.month, self.concept, self.amount, self.account)
The error I got:
raise ImproperlyConfigured(
django.core.exceptions.ImproperlyConfigured: The model Transaction is abstract, so it cannot be registered with admin.

Difference between NestedStackedInline and NestedTabularInline

I using a nested model in a Django project.
The following snippet code is models.py:
from django.db import models
from django.db.models.deletion import CASCADE
class Model_(models.Model):
name = models.CharField(max_length=50, default="This is a model")
frequently = models.FloatField(default=1.0)
def __str__(self):
return self.name
class SubModel(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=8, default='0x')
model_ = models.ForeignKey(Model_, on_delete=CASCADE)
def __str__(self):
return self.name
class Metadata(models.Model):
key = models.CharField(max_length=100)
value = models.CharField(max_length=100)
sub_model = models.ForeignKey(SubModel, on_delete=CASCADE)
This is my admin.py script:
from django.contrib import admin
from nested_inline.admin import NestedTabularInline, NestedStackedInline,\
NestedModelAdmin
from <djano-application-name>.models import Model_, SubModel, Metadata
class MetadataAdmin(NestedTabularInline):
model = Metadata
extra = 1
class SubModelAdmin(NestedStackedInline):
model = SubModel
inlines = [MetadataAdmin]
extra = 1
class Model_Admin(NestedModelAdmin):
model = Model_
inlines = [SubModelAdmin]
list_display = ['name']
admin.site.register(Model_, Model_Admin)
Question:
What is the difference between NestedStackedInline and NestedTabularInline in admin.py script?
[NOTE]:
Versions: Python 2.7 and Django 1.11
If you are using django-nested-inline, It means you wanted to edit models on the same page as a parent model and add more than 1 level of children at once with the parent object in admin.
The Django admin is just a normal Django application and you can't have a second level of inlines(nested forms) in the default Django admin.
The difference between NestedStackedInline and NestedTabularInline is just Layout. Indeed, both work exactly the same behind the scenes, the only difference is the template used for rendering. Check the official docs. So, picking one for your project is only a matter of preference regarding the interface layout.
This is how NestedStackedInline will look, each field of the model is under other.
and this is NestedTabularInline, each field of the model is in one line, column wise

With Django, representing foreignkeys in Admin

Building a generic app to practice learning with Django.
Two classes in Models:
class HouseInformation(models.Model):
house_name = models.CharField(max_length=200)
house_type = models.CharField(max_length=40)
address = models.CharField(max_length=200)
latitude = models.CharField(max_length=200)
longitude = models.CharField(max_length=200)
def __str__(self):
return self.house_name
class HouseReport(models.Model):
the_house = models.ForeignKey(HouseInformation)
visit_date = models.DateField()
In Admin view, I'd like to see a list of the houses with the dates they were visited. The admin.py so far is like so, and its not working:
from django.contrib import admin
from housing.models import HouseInformation
from housing.models import HouseReport
class HouseReport(admin.ModelAdmin)
list_display = ('the_house')
admin.site.register(HouseInformation, HouseReport)
I hope the one-to-many is represented correctly (one house can have many visits).
The problem is the missing ::
class HouseReport(admin.ModelAdmin):
^
Speaking about the task you've initially wanted to solve, check the InlineModelAdmin classes:
The admin interface has the ability to edit models on the same page as
a parent model. These are called inlines.
Add this to the admin.py:
from django.contrib import admin
from housing.models import HouseInformation, HouseReport
class HouseReportInline(admin.TabularInline):
model = HouseReport
class HouseAdmin(admin.ModelAdmin):
inlines = [
HouseReportInline,
]
admin.site.register(HouseInformation, HouseAdmin)
And you will see the House information and all of the HouseReports associated with a House on the House admin page.
You forgot the : after the class definition in line 5
class HouseReport(admin.ModelAdmin):
And you have to write
...
list_display = ('the_house',)
...
notice the trailing comma? It tells python, that it should create a tuple

Django reverse foreign key in admin

I have a Django related question about foreign keys in the admin panel. I'm facing the following situation:
class Driver(models.Model):
name = models.CharField(max_length=200)
executable = models.CharField(max_length=200)
class Device(models.Model):
name = models.CharField(max_length=200)
bound_driver = models.ForeignKey(Driver)
class DriverAssignment(models.Model):
device = models.ForeignKey(Device)
driver = models.ForeignKey(Driver)
Every device needs to have a bound driver (which it uses). DriverAssignment should be the table which shows which driver can be used by which device. So one device can have multiple possibilities of drivers which can be bound. Now i would like to have a dropdown on my admin panel showing all possible drivers for a specific device to select the 'bound_driver'.
How can i do this in Django? This is probably an easy thing for an experienced Django guy. I hope someone can give me a hint since i'm kind of new to Django. Thanks a lot!
For Django >1.8
Use the InlineModelAdmin (docs for 2.2) as explained there:
models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
admin.py
from django.contrib import admin
class BookInline(admin.TabularInline):
model = Book
class AuthorAdmin(admin.ModelAdmin):
inlines = [
BookInline,
]
Change your model Structure to This:
class Driver(models.Model):
name = models.CharField(max_length=200)
executable = models.CharField(max_length=200)
class Device(models.Model):
name = models.CharField(max_length=200)
bound_driver = models.ForeignKey(Driver, related_name="bound_to")
available_drivers = models.ManyToManyfield(Driver)
ManyToManyField would do the same work as DriverAssignment Table.
You can add Available drivers in Available drivers field.
But then You would also Want that bound_driver is one of the Available Drivers. This validation you will have to do in forms. For that you have to over-ride Admin forms. See links
Links of Reference:
ManytoMany field: https://docs.djangoproject.com/en/1.6/ref/models/fields/#django.db.models.ManyToManyField
Model Admin (to over-ride admin functionality):
https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#modeladmin-objects
You will have to spend some time reading and implementing if you want ot learn more. :)
OR
If you want to go with the same structure, than you will have to over-ride the form in ModelAdmin see here and Provide you custom form, which will be something like this:
class CustomForm(ModelForm)
bound_driver = forms.ModelChoiceField(queryset = <your custom queryset that returns only available drivers>, ...)
class Meta:
model = Device
https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form
There is a snippet for inverse inlines. If you still need it you may try this:
https://gist.github.com/mzbyszewska/8b6afc312b024832aa85
It has been used by me for OneToOneField in django 1.5 and 1.6. Unfortunately I did not test it for ForeignKeyField, but the one of the previous users claims that it works for ForeignKeyField either.
The best description of the snippet is contained in it. The Person class is your DriverAssignment class and Device correspond to the Address class in the example below:
Example:
from django.db import models
class Address(models.Model):
street = models.CharField(max_length = 255)
zipcode = models.CharField(max_length = 10)
city = models.CharField(max_length = 255)
class Person(models.Model):
name = models.CharField(max_length = 255)
business_addr = models.ForeignKey(Address,
related_name = 'business_addr')
home_addr = models.OneToOneField(Address, related_name = 'home_addr')
other_addr = models.OneToOneField(Address, related_name = 'other_addr')
You use reverseadmin in the following way:
from django.contrib import admin
from django.db import models
from models import Person
from reverseadmin import ReverseModelAdmin
class AddressForm(models.Form):
pass
class PersonAdmin(ReverseModelAdmin):
inline_type = 'tabular'
inline_reverse = ('business_addr', ('home_addr', AddressForm), ('other_addr' (
'form': OtherForm
'exclude': ()
)))
admin.site.register(Person, PersonAdmin)
inline_type can be either "tabular" or "stacked" for tabular and
stacked inlines respectively.

Categories

Resources