Question:
Models.py
Suggest i have got djanog class A:
class A(models.Model):
slug = models.SlugField(max_length=200, blank=True)
code = models.CharField("A", max_length=250)
name = models.CharField(("A"), max_length=250)
body = RichTextField(("A"), max_length=2500, blank=True, null=True)
policy = models.CharField(("A"), max_length=25, blank=True, null=True)
and i create class B:
class B(models.Model):
block = models.ManyToManyField(A)
In Admin portal, when creating an instance of class B, django chooses the ManyToMany field automatically to search based on name. I would like to add the fields based on the code of class A. Help please, I can't get it to work.
Thanks in advance for the tips!
Edit:
Admin.py
from django.contrib import admin
from import_export.admin import ImportExportMixin
from .models import A, B
admin.site.register(B)
class AAdmin(ImportExportMixin, admin.ModelAdmin):
list_display = ['code', 'name',]
search_fields = ['code', 'name', 'body']
admin.site.register(A, AAdmin)
Just figured it out by using raw_id_fields in admin.py :)
Related
I want to be able to search all models for all fields in Django admin, without having to setup ModelAdmin and searchfields individually.
example:
I have all my models in model.py:
# This is an auto-generated Django model module.
from django.db import models
class Diagnosis(models.Model):
id = models.BigAutoField(primary_key=True)
code = models.CharField(max_length=255)
starting_node = models.ForeignKey('Node', models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'diagnosis'
def __str__(self):
return 'Diag #' + str(self.id) + ' - ' + self.code
class DiagnosisHistory(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=255, blank=True, null=True)
date = models.DateTimeField(blank=True, null=True)
id_user = models.TextField(blank=True, null=True)
report = models.TextField(blank=True, null=True)
json_report = models.TextField(blank=True, null=True)
vin = models.CharField(max_length=20, blank=True, null=True)
class Meta:
managed = False
db_table = 'diagnosis_history'
# and so on
and the admin.py where I register the models:
from django.contrib import admin
from . import models
# Do not care. Register everything
for cls in [cls for name, cls in models.__dict__.items() if isinstance(cls, type)]:
admin.site.register(cls)
I don't want to run through each Model and manually create a ModelAdmin with each field
This is the solution I came up with:
from django.contrib import admin
from django.db import models as django_models
from . import models
relationship_fields = (django_models.ManyToManyField, django_models.ForeignKey, django_models.OneToOneField)
for cls in [cls for name, cls in models.__dict__.items() if isinstance(cls, type)]:
meta_fields = [field.name for field in cls._meta.local_fields if not isinstance(field, relationship_fields)]
class Admin(admin.ModelAdmin):
search_fields = meta_fields
admin.site.register(cls, Admin)
Note: registering all fields will fail since some are relationships. using cls._meta.local_fields exclude inferred relationships but you also need to exclude fields such as foreign keys defined in your model. Thus, we filter with isinstance(field, relationship_fields)
Note 2: I should probably use get_fields since local_fields seems to be private API (see https://docs.djangoproject.com/en/3.1/ref/models/meta/)
class MyModelAdmin(admin.ModelAdmin):
# ...
search_fields = [field.name for field in MyModel._meta.fields]
# ...
I've just set up the whole import-export thing and I just can't make it export a field from another model, using the foreign key.
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import date
from .validators import validate_file_size
# Create your models here.
class CORMeserii(models.Model):
CodCOR = models.CharField(max_length=25, primary_key=True, unique=True)
MeserieCor = models.CharField(max_length=50, unique=True)
def __str__(self):
return str(self.CodCOR + " - " + self.MeserieCor)
class Meta:
verbose_name_plural = "CORuri"
class Oferta(models.Model):
solicitant = models.ForeignKey(User, on_delete=models.CASCADE)
cor = models.ForeignKey(CORMeserii, on_delete=models.CASCADE)
dataSolicitare = models.DateField(default=date.today)
locuri = models.IntegerField()
agentEconomic = models.CharField(max_length=50)
adresa = models.CharField(max_length=150)
dataExpirare = models.DateField()
experientaSolicitata = models.CharField(max_length=200)
studiiSolicitate = models.CharField(max_length=200)
judet = models.CharField(max_length=20)
localitate = models.CharField(max_length=25)
telefon = models.CharField(max_length=12)
emailContact = models.EmailField(max_length=40)
rezolvata = models.BooleanField(default=False)
def __str__(self):
return str(self.cor)
admin.py
from django.contrib import admin
from .models import Oferta, CORMeserii
from import_export import resources
from import_export.admin import ImportExportMixin, ImportExportModelAdmin
import tablib
# Register your models here.
class CorImEx(resources.ModelResource):
class Meta:
model = CORMeserii
class CorAdmin(ImportExportMixin, admin.ModelAdmin):
list_display = ('CodCOR', 'MeserieCor')
resource_class = CorImEx
class CorImExAdmin(ImportExportModelAdmin):
resource_class = CorImEx
class OferteImEx(resources.ModelResource):
class Meta:
model = Oferta
fields = ('id', 'solicitant', 'cor', 'oferta.cor.MeserieCor')
class OfertaAdmin(ImportExportMixin, admin.ModelAdmin):
list_display = ('id', 'solicitant', 'dataExpirare', 'dataSolicitare')
resource_class = OferteImEx
class OferteImExAdmin(ImportExportModelAdmin):
resource_class = OferteImEx
admin.site.register(Oferta, OfertaAdmin)
admin.site.register(CORMeserii, CorAdmin)
You can see it in the OferteImEx class - the field 'oferta.cor.MeserieCor'.
I want to export the MeserieCor field from the model CORMeserii - but I can't figure it out.
I tried with: 'oferta.cor.MeserieCor',
cor.MeserieCor', 'MeserieCor' (even though the last 2 ones don't make sense to me at all).
Is there any way to export that field somehow, even though it is from another model? (I'm pretty sure there is but I can't figure it out)
Thanks.
In Django you use double underscore (__) to follow relationship in lookups. It's in the documentation:
Django offers a powerful and intuitive way to “follow” relationships in lookups, taking care of the SQL JOINs for you automatically, behind the scenes. To span a relationship, just use the field name of related fields across models, separated by double underscores, until you get to the field you want.
Link: Lookups that span relationship
There is even an example in the django import export documentation how to follow relationship:
When defining ModelResource fields it is possible to follow model relationships:
class BookResource(resources.ModelResource):
class Meta:
model = Book
fields = ('author__name',)
Source: https://django-import-export.readthedocs.io/en/latest/getting_started.html#customize-resource-options
I'm trying to model a rent contract in Django and use the admin form to insert and modify it.
Both owner and tenant can be companies (VAT number) or individuals (no VAT number). Companies and individuals are stored in two different models (Company and Individual).
I'm trying to solve this problem using Generic Foreign Key but I'm not able to show the tenant name in the admin page, only an integer field not friendly at all.
gestimm is the name of the app and that's my oversimplified models:
# my gestimm/models.py
#
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Individual(models.Model):
name = models.CharField(max_length=100, help_text='Name')
def __str__(self):
return self.name
class Company(models.Model):
name = models.CharField(max_length=100, help_text='Name')
def __str__(self):
return self.name
class Contract(models.Model):
description = models.CharField(max_length=30)
start = models.DateField()
stop = models.DateField()
def __str__(self):
return self.description
class Tenant(models.Model):
limit = models.Q(app_label='gestimm', model='individual') | models.Q(app_label='gestimm', model='company')
contract = models.ForeignKey(Contract, on_delete=models.CASCADE,
null=True, blank=True)
content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT,
help_text='Tenant', null=True,
limit_choices_to=limit)
object_id = models.PositiveIntegerField(null=True)
tenant = GenericForeignKey('content_type', 'object_id')
How I tried to solve the problem:
# my gestimm/admin.py
#
from django.contrib import admin
from .models import Individual, Company, Contract, Tenant
class TenantInline(admin.StackedInline):
model = Tenant
extra = 1
class ContractAdmin(admin.ModelAdmin):
inlines = [TenantInline]
admin.site.register(Individual)
admin.site.register(Company)
admin.site.register(Contract, ContractAdmin)
I found some old discussions but none of the proposed solutions worked.
Problem solved: I installed django-grappelli.
My new admin.py:
class TenantInline(admin.TabularInline):
model = Tenant
extra = 1
related_lookup_fields = {
'generic': [['content_type', 'object_id']],
}
class ContractAdmin(admin.ModelAdmin):
inlines = [
TenantInline,
]
admin.site.register(Contract, ContractAdmin)
As intended
Hi i have a django model for notification which have a many-to-many relation but nothing appears in django admin ( all fields do not appear)
class Notification(models.Model):
"""send notification model"""
title = models.CharField(max_length=200)
text = models.TextField(null=True, blank=True)
device = models.ManyToManyField(Device, null=True, blank=True)
country = models.ManyToManyField(Country, null=True, blank=True)
sent = models.BooleanField(default=False)
when i open django admin for this model and press add notification this is what happens (nothing appears)
Country and Device Code
class Device(models.Model):
"""Store device related to :model:`accounts.User`."""
user = models.OneToOneField(User, related_name='device', on_delete=models.CASCADE)
model = models.CharField(max_length=200, blank=True, null=True)
player_id = models.CharField(max_length=200, blank=True, null=True)
class Meta:
verbose_name = 'Device'
verbose_name_plural = 'Devices'
def __str__(self):
return self.model
class Country(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
Admin.py
from django.contrib import admin
from .models import Notification
admin.site.register(Notification)
Edit:
Thank you all the problem is solved
The problem was caused by some entries in device model that did have None in the model field so there was a problem displaying it correctly.
According to https://code.djangoproject.com/ticket/2169 :
When a class has a field that isn't shown in the admin interface but
must not be blank, it's impossible to add a new one. You get a cryptic
"Please correct the error below." message with no shown errors. The
error message should probably say something about a hidden field.
Now ManyToManyFields don't need null=True, try removing those statements and see if you get an improvement.
Also, try adding the Country and Device models in admin.py so admin can see them and display them.
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#working-with-many-to-many-models
Define an inline for the many-to-manys in admin.py:
from django.contrib import admin
class DeviceInline(admin.TabularInline):
model = Notification.device.through
class CountryInline(admin.TabularInline):
model = Notification.country.through
class NotificationAdmin(admin.ModelAdmin):
inlines = [
DeviceInline, CountryInline
]
exclude = ("device", "country")
I tried following an answer at this previous post:
DateTimeField doesn't show in admin system
But maybe I'm just too dim to understand it.
No field of created_at shows up. Could anyone point me in the right direction?
model
class holding_transaction(models.Model):
holdingname = models.ForeignKey(holding, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
admin.py
class holding_transactionAdmin(admin.ModelAdmin):
readonly_fields = ('created_at', )
admin.site.register(holding_transaction, holding_transactionAdmin)
Edit:
Update:
Here is the code that worked for me for an imaginary application called Beatles:
beatles/models.py:
from django.db import models
# Create your models here.
class Person(models.Model):
name = models.CharField(max_length=128)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self): # __unicode__ on Python 2
return self.name
beatles/admin.py
from django.contrib import admin
# Register your models here.
from beatles.models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
readonly_fields = ('created_at', )
The answer to the question mentioned, states that this is not possible to happen.
Nonetheless, if you want to edit such fields, according to the docs you proceed as follows:
If you want to be able to modify this field, set the following instead
of auto_now_add=True:
For DateField: default=date.today - from datetime.date.today()
For DateTimeField: default=timezone.now - from django.utils.timezone.now()
If you want those fields just to be displayed, you can use the following code:
class YourModelAdmin(admin.ModelAdmin):
readonly_fields = ('created_at', 'updated_at', )
admin.site.register(YourModel, YourModelAdmin)