I created an App called order whose models.py (model name create_order) contain fields like order_created_by, cloth_type, clothe colour, size, delivery_date, order_created_date. Now in models.py of warehouse app, I want to see all the field of created_order model. How can I do that? And can do this all using first importing models in views.py of warehouse and the creating function then returning the HttpResponse using models.objects.all(). But I want to see these all fields of create_order in admin.py of warehouse app.
To achieve this you can use Django Model inheritance. For example, in your orders/models.py file you can create an Abstract model that will contain all the common fields. But to make your project clean and structured its a nice idea to create another app with the name of core or utils (most common name in the Django community) and put all common files there.
class CommonFieldModel(models.Model):
order_created_by = ...
cloth_type = ...
clothe = ...
colour = ...
size = ...
delivery_date = ...
order_created_date = ...
class Meta:
abstract = True
# order/models.py
class YourOrderAppModel(CommonFieldModel):
# Will contain all CommonField Model fields + all other field you define here.
...
# warehouse/models.py
class YourWareHouseModel(CommonFieldModel):
# this model will contain all CommonField model fields
# You can also define other fields here.
...
By default abstract is False.
Related
I need to display multiple models in django admin change list view. I want to use single search box to filter all of them at once. Is there an easy way to do it?
My idea was to inherit from admin site, add another view to it and iterate over models in modified change_list.html but i can't import models and ModelAdmins because i get django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. error so i can't get the same context that django uses to render regular change_list.html.
What's the correct way to do it? Is there simpler approach?
As Ohad suggested, the most robust approach is probably to make formal relationships between the models from which you want the objects to display together. You have a couple of options here. Essentially you will want to make a master class and then subclass your models from it. This makes a lot of sense if your models are ontologically related to a parent concept. For example:
Publication
Book
Magazine issue
Books and magazines are both publications. They both share some fields, like title and publication date. But they differ in that a book usually has a single author and a magazine has volumes and issue dates. Django already provides a couple different approaches to subclassing using Model inheritance. However, after trying these myself I found that the django-polymorphic extension is way better. Here is a code example of a Django 3.0 app using django-polymorphic which has a Book model and a Magazine model with a single listing of all publications that shows all of the books and magazines in the system.
models.py
from django.db import models
from polymorphic.models import PolymorphicModel
class Publication(PolymorphicModel):
title = models.CharField(max_length=256)
publication_year = models.IntegerField()
class Book(Publication):
author_first = models.CharField(max_length=256)
author_last = models.CharField(max_length=256)
class Magazine(Publication):
volume_number = models.IntegerField()
issue_name = models.CharField(max_length=256)
admin.py
from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
from .models import Publication, Book, Magazine
class PublicationChildAdmin(PolymorphicChildModelAdmin):
""" Base admin class for all child models """
base_model = Publication # Optional, explicitly set here.
#admin.register(Book)
class BookAdmin(PublicationChildAdmin):
base_model = Book # Explicitly set here!
# show_in_index = True # makes child model admin visible in main admin site
list_display = ('title', 'publication_year', 'author_first', 'author_last')
#admin.register(Magazine)
class MagazineAdmin(PublicationChildAdmin):
base_model = Magazine # Explicitly set here!
# show_in_index = True # makes child model admin visible in main admin site
list_display = ('title', 'publication_year', 'issue_name')
#admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = Publication # Optional, explicitly set here.
child_models = (Book, Magazine)
list_filter = (PolymorphicChildModelFilter,) # This is optional.
list_display = ('title', 'publication_year')
This will of course only display those fields that are common (in the Publication model). If you want to display fields that are particular to each model there are various tricks for this. Here's one quick way to do it:
admin.py
...
#admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = Publication # Optional, explicitly set here.
child_models = (Book, Magazine)
list_filter = (PolymorphicChildModelFilter,) # This is optional.
list_display = ('title', 'publication_year', 'issue', 'author')
def author(self, obj):
if obj.polymorphic_ctype.model == 'book':
book = Book.objects.get(pk=obj.pk)
return book.author_first + ' ' + book.author_last
return None
def issue(self, obj):
if obj.polymorphic_ctype.model == 'magazine':
return str(Magazine.objects.get(pk=obj.pk).issue_name)
return None
Tada!
From the docs it seems that there is no easy solution.(if there is no relation between the models)
https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields
So if the search is commonly used build a special model/models that combines the data that might be searched
Giving the following models in Django :
class MyModel(models.Model):
name = models.CharField('This is my name'),max_length=150)
class AnotherModel(models.Model):
my_model_field_name = [...]
I'm tring to store in AnotherModel.my_model_field_name the name of MyModel.name field (so 'This is my name').
I would like it to be linked, so if tomorow I change the name of MyModel.name field by "This is my new name", I want that all my previous records of my AnotherModel.my_model_field_name automatically update.
Model instances are able to link to other models instances, not models themselves, right ?
Is it possible or just stupid ?
EDIT :
I found a solution : the Django ContentType table is just perfect to do that.
With content type, you can iter over field of a model, without model instance (I mean, a line in my MyModel table), so, I can for example, do something like that :
from django.contrib.contenttypes.models import ContentType
from .models import MyModel
# get the model I want
my_model = ContentType.objects.get_for_model(MyModel)
# get all fields of this model
fields = model._meta.get_fields()
# Iterate over the fields to find the one I want, and read it's specifications
for field in fields:
# all my stuff here
I think the right way to do this is:
class MyModel(models.Model):
name = models.CharField('This is my name'),max_length=150)
class AnotherModel(models.Model):
my_model = models.ForeignKey(MyModel)
So if you need to read the MyModel name field from AnotherModel you can do:
another_model = AnotherModel.objects.last()
another_model.my_model.name
In this way AnotherModel has a link to MyModel and when you change MyModel name field it will be reflected in AnotherModel objects.
I use Django 1.10. I have the following model structure:
class GenericPage(models.Model):
"""Abstract page, other pages inherit from it."""
book = models.ForeignKey('Book', on_delete=models.CASCADE)
class Meta:
abstract = True
class GenericColorPage(models.Model):
"""Abstract page that is sketchable and colorable, other pages inherit from it."""
sketched = models.BooleanField(default=False)
colored = models.BooleanField(default=False)
class Meta:
abstract = True
class GenericBookPage(GenericColorPage):
"""A normal book page, with a number. Needs to be storyboarded and edited."""
###
#various additional fields
###
class Meta:
# unique_together = (('page_number', 'book'),) # impedes movement of pages
ordering = ('-book', '-page_number',)
abstract = True
objects = BookPageManager() # the manager for book pages
class BookPage(GenericBookPage):
"""Just a regular book page with text (that needs to be proofread)"""
proofread = models.BooleanField(default=False)
Additionally, an excerpt from Admin:
class BookPageAdmin(admin.ModelAdmin):
# fields NOT to show in Edit Page.
list_display = ('__str__', 'page_name', 'sketched', 'colored', 'edited', 'proofread',)
list_filter = ('book',)
readonly_fields = ('page_number',) # valid page number is assigned via overridden save() in model
actions = ['delete_selected',]
I tried to do ./manage.py makemigrations but if throws the following errors:
<class 'progress.admin.BookPageAdmin'>: (admin.E116) The value of 'list_filter[0]' refers to 'book', which does not refer to a Field.
progress.BookPage: (models.E015) 'ordering' refers to the non-existent field 'book'.
In the past, when I did not use the abstracts and just put everything into BookPage model, it all worked fine. But it seems that Meta and Admin don't see the fields in parent classes. Am I missing something? Is there a way to make them read fields from abstract parents?
In the past, when I did not use the abstracts and just put everything into BookPage model, it all worked fine
Of course it worked fine because you put everything inside BookPage which is not an abstract class which means that table (and, thus, fields) will be created.
But it seems that Meta and Admin don't see the fields in parent classes. Am I missing something?
You're missing the fact that none of your models inherits from the GenericPage abstract model. Thus, the book field is never created.
Is there a way to make them read fields from abstract parents?
You must create/modify a model that inherits from an abstract model. Maybe, do this:
class GenericBookPage(GenericColorPage, GenericPage):
which allows you to inherit both GenericColorPage and GenericPage fields. When I say inherit I mean when the migrate command runs to actually create the database table and the relevant columns (model fields).
views.py
I'm creating a queryset that I want to serialize and return as JSON. The queryset looks like this:
all_objects = Program.objects.all()
test_data = serializers.serialize("json", all_objects, use_natural_keys=True)
This pulls back everything except for the 'User' model (which is linked across two models).
models.py
from django.db import models
from django.contrib.auth.models import User
class Time(models.Model):
user = models.ForeignKey(User)
...
class CostCode(models.Model):
program_name = models.TextField()
...
class Program(models.Model):
time = models.ForeignKey(Time)
program_select = models.ForeignKey(CostCode)
...
Question
My returned data has Time, Program, and CostCode information, but I'm unable to query back the 'User' table. How can I get back say the 'username' (from User Table) in the same queryset?
Note: I've changed my queryset to all_objects = Time.objects.all() and this gets User info, but then it doesn't pull in 'CostCode'. My models also have ModelManagers that return the get_by_natural_key so the relevant fields appear in my JSON.
Ultimately, I want data from all four models to appear in my serialized JSON fields, I'm just missing 'username'.
Here's a picture of how the JSON object currently appears in Firebug:
Thanks for any help!
It seems a bit heavyweight at first glance but you could look at using Django REST Framework:
http://www.django-rest-framework.org/api-guide/serializers#modelserializer
You can define and use the serializer classes without having to do anything else with the framework. The serializer returns a python dict which can then be easily dumped to JSON.
To get all fields from each related model as nested dicts you could do:
class ProgramSerializer(serializers.ModelSerializer):
class Meta:
model = Program
depth = 2
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
To customise which fields are included for each model you will need to define a ModelSerializer class for each of your models, for example to output only the username for the time.user:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', )
class TimeSerializer(serializers.ModelSerializer):
"""
specifying the field here rather than relying on `depth` to automatically
render nested relations allows us to specify a custom serializer class
"""
user = UserSerializer()
class Meta:
model = Time
class ProgramSerializer(serializers.ModelSerializer):
time = TimeSerializer()
class Meta:
model = Program
depth = 1 # render nested CostCode with default output
all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
What you really want is a "deep" serialization of objects which Django does not natively support. This is a common problem, and it is discussed in detail here: Serializing Foreign Key objects in Django. See that question for some alternatives.
Normally Django expects you to serialize the Time, CostCode, Program, and User objects separately (i.e. a separate JSON array for each) and to refer to them by IDs. The IDs can either be the numeric primary keys (PKs) or a "natural" key defined with natural_key.
You could use natural_key to return any fields you want, including user.username. Alternatively, you could define a custom serializer output whatever you want there. Either of these approaches will probably make it impossible to load the data back into a Django database, which may not be a problem for you.
I'm trying to dynamically generate a new Model, based on fields from an existing Model. Both are defined in /apps/main/models.py. The existing model looks something like this:
from django.db import models
class People(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
height = models.IntegerField()
I have a list containing the names of fields that I would like to copy:
target_fields = ["name", "age"]
I want to generate a new model the has all of the Fields named in target_fields, but in this case they should be indexed (db_index = True).
I originally hoped that I would just be able to iterate over the class properties of People and use copy.copy to copy the field descriptions that are defined on it. Like this:
from copy import copy
d = {}
for field_name in target_fields:
old_field = getattr(People, field_name) # alas, AttributeError
new_field = copy(old_field)
new_field.db_index = True
d[field_name] = new_field
IndexedPeople = type("IndexedPeople", (models.Model,), d)
I wasn't sure if copy.copy()ing Fields would work, but I didn't get far enough to find out: the fields listed in the class definition don't aren't actually included as properties on the class object. I assume they're used for some metaclass shenanigans instead.
After poking around in the debugger, I found some type of Field objects listed in People._meta.local_fields. However, these aren't just simple description that can be copy.copy()ed and used to describe another model. For example, they include a .model property referring to People.
How can I create a field description for a new model based on a field of an existing model?
From poking around in the debugger and the source: all Django models use the ModelBase metaclass defined in /db/models/base.py. For each field in a model's class definition, ModelBase's .add_to_class method will call the field's .contribute_to_class method.
Field.contribute_to_class is defined in /db/models/fields/__init__.py and it is what's responsible for associating a field definition with a particular model. The field is modified by adding the .model property and by calling the .set_attributes_from_name method with the name used in the model's class definition. This in turn adds adds the .attname and .column properties and sets .name and .verbose_name if necessary.
When I inspect the __dict__ property of a newly-defined CharField and compare it with that of a CharField that was already associated with a model, I also see that these are the only differences:
The .creation_counter property is unique for each instance.
The .attrname, .column and .model properties do not exist on the new instance.
The .name and .verbose_name properties is None on the new instance.
It doesn't seem possible to distinguish between .name/.verbose_name properties that were manually specified to the constructor and ones that were automatically generated. You'll need to chose either to always reset them, ignoring any manually-specified values, or never clear them, which would cause them to always ignore any new name they were given in the new model. I want to use the same name as the original fields, so I am not going to touch them.
Knowing what differences exist, I am using copy.copy() to clone the existing instance, then apply these changes to make it behave like a new instance.
import copy
from django.db import models
def copy_field(f):
fp = copy.copy(f)
fp.creation_counter = models.Field.creation_counter
models.Field.creation_counter += 1
if hasattr(f, "model"):
del fp.attname
del fp.column
del fp.model
# you may set .name and .verbose_name to None here
return fp
Given this function, I create the new Model with the following:
target_field_name = "name"
target_field = People._meta.get_field_by_name(target_field_name)[0]
model_fields = {}
model_fields["value"] = copy_field(target_field)
model_fields["value"].db_index = True
model_fields["__module__"] = People.__module__
NewModel = type("People_index_" + field_name, (models.Model,), model_fields)
It works!
Solution
There is build in way for fields copying Field.clone() - method which deconstructs field removing any model dependent references:
def clone(self):
"""
Uses deconstruct() to clone a new copy of this Field.
Will not preserve any class attachments/attribute names.
"""
name, path, args, kwargs = self.deconstruct()
return self.__class__(*args, **kwargs)
So you can use following util to copy fields ensuring that you'll not accidentally affect source fields of model you're copying from:
def get_field(model, name, **kwargs):
field = model._meta.get_field(name)
field_copy = field.clone()
field_copy.__dict__.update(kwargs)
return field_copy
Also can pass some regular kwargs like verbose_name and etc:
def get_field_as_nullable(*args, **kwargs):
return get_field(*args, null=True, blank=True, **kwargs)
Does not work for m2m fields inside of model definition. (m2m.clone() on model definition raises AppRegistryNotReady: Models aren't loaded yet)
Why this instead of abstract models?
Well, depends on case. Some times you don't need inheristance but actuall fields copying. When? For example:
I have a User model and model which represents an application (document for user data update request) for user data update:
class User(models.Model):
first_name = ...
last_name = ...
email = ...
phone_number = ...
birth_address = ...
sex = ...
age = ...
representative = ...
identity_document = ...
class UserDataUpdateApplication(models.Model):
# This application must ONLY update these fields.
# These fiends must be absolute copies from User model fields.
user_first_name = ...
user_last_name = ...
user_email = ...
user_phone_number = ...
So, i shouldn't carry out duplicated fields from my User model to abstract class due to the fact that some other non-user-logic-extending model wants to have exact same fields. Why? Because it's not directly related to User model - User model shouldn't care what depends on it (excluding cases when you want to extend User model), so it shouldn't be separated due to fact that some other model with it's own non User related logic want's to have exact same fields.
Instead you can do this:
class UserDataUpdateApplication(models.Model):
# This application must ONLY update these fields.
user_first_name = get_field(User, 'first_name')
user_last_name = get_field(User, 'last_name')
user_email = get_field(User, 'user_email')
user_phone_number = get_field(User, 'phone_number')
You also would make som util which would generate some abc class "on fly" to avoid code duplication:
class UserDataUpdateApplication(
generate_abc_for_model(
User,
fields=['first_name', 'last_name', 'email', 'phone_number'],
prefix_fields_with='user_'),
models.Model,
):
pass