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]
# ...
Related
The field which is specified in my models file is not included in the GraphiQL, I have tried to rename the field, delete it and define it again, even changing the type of field also updating the graphene-django package. None of these I have mentioned didn't work. The name of the field I can't get is named book
models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from books.models import Book
class Borrowing(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
book = models.OneToOneField(Book, null=True, on_delete=models.CASCADE)
date = models.DateTimeField(default=timezone.now)
returned = models.BooleanField(default=False)
date_borrowed = models.CharField(blank=True, null=True, max_length=50)
date_returned = models.CharField(blank=True, null=True, max_length=50)
class Meta:
ordering = ['-date']
def __str__(self):
return f'{self.user.username} borrowed {self.book.title}'
schema.py
import graphene
from .mutations.borrowings import *
from backend.functions import pagination
PAGE_SIZE = 12
class BorrowingMutation(graphene.ObjectType):
borrow_book = BorrowBook.Field()
return_book = ReturnBook.Field()
class BorrowingQuery(graphene.ObjectType):
borrowings = graphene.List(BorrowingType)
users_borrowings = graphene.List(BorrowingType, page=graphene.Int())
def resolve_borrowings(self, info):
return Borrowing.objects.all()
def resolve_users_borrowings(self, info, page):
user = info.context.user
borrowings = Borrowing.objects.filter(user=user, returned=False)
borrowings = pagination(PAGE_SIZE, page, borrowings)
return borrowings
Type
class BorrowingType(DjangoObjectType):
class Meta:
model = Borrowing
In django how to make form field exact, i.e it will have choices?
My forms.py:
from django import forms
class FilterForm(forms.Form):
category = forms.CharField()
price = forms.IntegerField()
My models.py:
CATEGORY_CHOICES = (
('Fruits and Vegetables', 'Fruits and Vegetables'),
('Electronics', 'Electronics'),
('Clothing', 'Clothing'),
('Books', 'Books'),
)
class Item(models.Model):
title = models.CharField(max_length=120)
price = models.FloatField()
discount_price = models.FloatField(blank=True, null=True)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=120, null=True, blank=True)
image_url = models.CharField(max_length=2083, null=True, blank=True)
slug = models.SlugField(null=True, blank=True)
description = models.TextField(null=True, blank=True)
Please make use of a ModelForm [Django-doc]. A ModelForm is capable of automating a lot of aspects when creating or update model records. Furthermore it can automatically construct the fields based on the fields of the model. You can, if you want to, alter the widgets, etc. But usually a ModeLField is a good starting point.
Here you thus can construct a form like:
# app/forms.py
from django import forms
from app.models import Item
class FilterForm(forms.ModelForm):
class Meta:
model = Item
fields = ['category', 'price']
Where you replace app with the name of the app.
You can use ModelForm
from django import forms
from .models import Item
class FilterForm(forms.ModelForm):
class Meta:
model = Item
fields = [
'category',
'price'
]
If you wanna stick with Form, use Choice Field and copy the Choices in form
from django import forms
class FilterForm(forms.Form):
CATEGORY_CHOICES = (
('Fruits and Vegetables', 'Fruits and Vegetables'),
('Electronics', 'Electronics'),
('Clothing', 'Clothing'),
('Books', 'Books'),
)
category = forms.ChoiceField(choices=CATEGORY_CHOICES)
price = forms.IntegerField()
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 am new to django and am trying to set up a simple employee timesheet site that has multiple users. I set up two models one for the individual employee that has a ForeignKey of the base django user and a timesheet model that has a ForeignKey of the employee model. I'm not sure this is correct because when I use my registration form it just creates the base django user and not the "Employee" so when I want to create a new timesheet entry only the one employee is set up (set up with admin page). Can someone with more django experience tell me if there is a better way to do this (different model relationship, etc)
from django.urls import reverse
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils import timezone
import datetime
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='employee')
payRate = models.DecimalField(max_digits=4, decimal_places=2, default=15.00, verbose_name=("Pay"))
vacTotal = models.DecimalField(max_digits=5, decimal_places=2, default=200.00, verbose_name=("Vacation"))
# META CLASS
class Meta:
verbose_name = 'employee'
verbose_name_plural = 'employees'
# TO STRING METHOD
def __str__(self):
return f"{self.user}"
class Tsheet(models.Model):
# CHOICES
WORK_CHOICES= (
('W', 'Regular Work'),
('V', 'Vacation'),
('S', 'Sick',),
('C','Call In'),
)
# DATABASE FIELDS
name = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='name')
workType = models.CharField(max_length=15,choices=WORK_CHOICES)
workDate = models.DateField(verbose_name=("Date"), default=datetime.date.today, editable=True)
workDescription = models.CharField(max_length=200)
workHours = models.DecimalField(max_digits=4, decimal_places=2, default=8.00, verbose_name=("Hours"))
workReviewed= models.BooleanField(default=False)
slug = models.SlugField(max_length=50, unique=True,
help_text='Unique value for timesheet entry URL, created automatically from name.')
# META CLASS
class Meta:
verbose_name = 'tsheet'
verbose_name_plural = 'tsheets'
# TO STRING METHOD
def __str__(self):
return f"{self.name} - {self.workDate} - {self.workHours} - {self.workType}"
# SAVE METHOD
# ABSOLUTE URL METHOD
def get_absolute_url(self):
return reverse('entry-detail', kwargs={'pk': self.pk})```
The right way to approach this is to extend the AbstractUser and add the fields there:
class User(AbstractUser):
payRate = models.DecimalField(max_digits=4, decimal_places=2, default=15.00, verbose_name=("Pay"))
vacTotal = models.DecimalField(max_digits=5, decimal_places=2, default=200.00, verbose_name=("Vacation"))
Then you have a single table with all the data from the default Django User as well as your specific fields
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