Django, Python inheritance: Exclude some fields from superclass - python

I have inheritance between Employee and Manager classes. Employee - superclass, Manager - subclass.
class Employee(models.Model):
name = models.CharField(max_length=50, null=False)
address = models.CharField(max_length=50, null=False)
class Manager(Employee):
department = models.CharField(max_length=50)
"""
Here I don't want the 'name' and 'address' fields of Employee class.
(I want other fields of Employee and 'department' field of this
class to be stored in Manager table in database)
"""
How can achieve this. Thanks in advance.

You can make private variables in python class using 2 underscores (__), check this example for more.
However they will store that values in child object as there is no such thing as private or protected in Python.
But another approach can work for Django. In Django model fields are stored depending on their value (CharField, DateField and etc.) but if you will make item value None or any other static value (ex. "string"), that should solve your problem:
class Manager(Employee):
name = None
address = None
# other_stuffs.
In that example, Manager should not have name and address columns in database and when you will try to access them, you will get None. And if you want to get AttributeError (Django raises that when object hasn't requested key) then you can also add property:
class Manager(Employee):
name = None
#property
def name(self):
raise AttributeError("'Manager' object has no attribute 'name'")

I'd use 3 classes:
class BaseEmployee(models.Model):
# All your common fields
class Employee(BaseEmployee):
name = models.CharField(max_length=50, null=False)
address = models.CharField(max_length=50, null=False)
class Manager(BaseEmployee):
department = models.CharField(max_length=50)
I think that achieves what you wanted.

You need to use 3 classes "AbstractEmployee" with "abstract = True", "Employee" and "Manager" as shown below. "abstract = True" makes "AbstractEmployee" class an abstract class so a table is not created from "AbstractEmployee" class while each table is created from "Employee" and "Manager" classes and to remove the inherited fields "name" and "address" from "Manager" class, set "None" to them:
class AbstractEmployee(models.Model):
name = models.CharField(max_length=50, null=False)
address = models.CharField(max_length=50, null=False)
class Meta:
abstract = True # Here
class Employee(AbstractEmployee):
pass
class Manager(AbstractEmployee):
name = None # Here
address = None # Here
department = models.CharField(max_length=50)

Related

Dehydrate the same field django

I have a model Student:
class Student(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30, verbose_name='Name')
lastname = models.CharField(max_length=100, verbose_name='Lastname')
history = HistoricalRecords()
Also I have a model:
class Class(models.Model):
id = models.AutoField(primary_key=True)
student = models.models.ForeignKey(Student,on_delete = models.CASCADE, related_name='+')
my admin.py
class ClassResource(resources.ModelResource):
class Meta:
model = Class
fields = ('student',)
def dehydrate_student(self, Class):
student= getattr(Class.student, "name")
return '%s' % (student)
class ClassExportAdmin(ImportExportModelAdmin, ClassAdmin):
resource_class = ClassResource
admin.site.register(Class, ClassExportAdmin)
Now I am executing only name, is that possible to dehydrate the same field student one more time. I need past into my 2 column the surname of the student.
To export both Student.name and Student.lastname you can directly reference a Foreign Key relation in the fields parameter (docs):
class ClassResource(resources.ModelResource):
class Meta:
model = Class
fields = ('student__name', 'student__lastname')
This means that the column names will appear in your export as:
student__name
student__lastname
If you want the name to be different, you can directly declare a field:
name = Field(
column_name='name',
attribute='name',
widget=ForeignKeyWidget(Student, 'name'))
This will then appear in the export under name.

Creating a models.UniqueConstraint in abstract model

I am creating an abstract model for my django app, SrdObject. One of the characteristics of my model is that it has a pair of fields that, taken together, must be unique: 'name' and the foreign key 'module'.
Here is an example of what I had
class SrdObject(models.Model):
name = models.CharField(max_length=50)
slug_name = models.SlugField(max_length=75, unique=True)
active = models.BooleanField(default=True)
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='%(class)s', blank=False, null=False, default='default')
class Meta:
unique_together = ['name', 'module']
ordering = ['name']
abstract = True
This seemed to be working ok, but the unique_together attribute has been marked as deprecated by django (See here), so I changed it to this
class Meta:
constraints = [
models.UniqueConstraint(fields=['name', 'module'], name='unique-in-module')
]
ordering = ['name']
abstract = True
This doesn't work because the name field must be unique, and since this is an abstract class, the constraint is repeated over several models.
I also tried
models.UniqueConstraint(fields=['name', 'module'], name='{}-unique-in-module'.format(model_name))
But obviously this falls into scoping problems, so I tried a decorator method
def add_unique_in_module_constraint(cls):
cls._meta.constraints = [
models.UniqueConstraint(fields=['name', 'module'], name='unique-in-module')
]
return cls
#add_unique_in_module_constraint
class SrdObject(models.Model):
class Meta:
ordering = ['name']
abstract = True
But this didn't seem to do anything.
So how do I create a models.UniqueConstraint in abstract model if every constraint needs a unique name attribute?
LATEST UPDATE
Since 3rd version you finally can do that by specifying interpolation:
Changed in Django 3.0:
Interpolation of '%(app_label)s' and '%(class)s' was added.
Example:
UniqueConstraint(fields=['room', 'date'], name='%(app_label)s_unique_booking')
OLD (Django < 3.0)
You can't do that, same problem for me, so sad...
Source: django docs
Constraints in abstract base classes
You must always specify a unique name for the constraint. As such, you cannot normally specify a constraint on an abstract base class, since the Meta.constraints option is inherited by subclasses, with exactly the same values for the attributes (including name) each time. Instead, specify the constraints option on subclasses directly, providing a unique name for each constraint.
I took this model setup:
class Module(models.Model):
pass
class SrdObject(models.Model):
name = models.CharField(max_length=50)
slug_name = models.SlugField(max_length=75, unique=True)
active = models.BooleanField(default=True)
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='%(class)s', blank=False, null=False, default='default')
class Meta:
constraints = [
models.UniqueConstraint(fields=['name', 'module'], name='unique-in-module')
]
ordering = ['name']
abstract = True
class SrdObjectA(SrdObject):
pass
class SrdObjectB(SrdObject):
pass
And then ran these tests:
class TestSrdObject(TestCase):
#classmethod
def setUpTestData(cls):
cls.module = Module.objects.create()
SrdObjectA.objects.create(name='A', module=cls.module)
def test_unique_applies_to_same_model(self):
with self.assertRaises(IntegrityError):
SrdObjectA.objects.create(name='A', module=self.module)
def test_unique_does_not_apply_to_different_model(self):
self.assertTrue(SrdObjectB.objects.create(name='A', module=self.module))
And they pass. Perhaps I'm still missing the problem you're running into?

Django: How to create a dynamic related_name for an inherited parent model?

I have 4 models:
class User(models.Model):
name = models.CharField(max_length=255)
class A(models.Model):
user= models.ForeignKey("User", related_name="u_a", on_delete=models.CASCADE)
title = models.CharField(max_length=255)
class B(A):
user= models.ForeignKey("User", related_name="u_b", on_delete=models.CASCADE)
#isn't the code repeated???
b_field = CharField(max_length=255)
class C(A):
user= models.ForeignKey("User", related_name="u_c", on_delete=models.CASCADE)
#isn't the code repeated???
c_field = CharField(max_length=255)
Here, A has a ForeignKey relationsip with User and a reverse relationship as u_a. But B and C are children of A.
So It appears to me as if Do not repeat your code is violated. How to overcome this?
To work around this problem, In your model class A(models.Model) The part of the value should contain '%(app_label)s' and/or '%(class)s'. see the doc
'%(class)s' is replaced by the lower-cased name of the child class that the field is used in.
'%(app_label)s' is replaced by the lower-cased name of the app the child class is contained within. Each installed application name must be unique and the model class names within each app must also be unique, therefore the resulting name will end up being different.
class A(models.Model):
user= models.ForeignKey("User", related_name="%(class)s_set",
on_delete=models.CASCADE)
#user= models.ForeignKey("User", related_name="%(app_label)s_%(class)s_set",
#on_delete=models.CASCADE)
title = models.CharField(max_length=255)
class B(A):
b_field = CharField(max_length=255)
class C(A):
c_field = CharField(max_length=255)

Django Inheriting from classes

I have run into a problem developing my Django site.
from django.db import models
class TitlePost(models.Model):
title_name = models.CharField(max_length=100, unique=True)
title_body = models.TextField(max_length=30000)
title_why = models.TextField(max_length=250, null=True)
title_publication_date = models.DateTimeField('date')
likes = models.IntegerField(default=0)
dislikes = models.IntegerField(default=0)
def __unicode__(self):
return self.title_name
class TopTitlesPostPage(models.Model):
title_post = models.OneToOneField(TitlePost)
hello = models.CharField(max_length=100, unique=True)
def __unicode__(self):
return self.hello
class NewTitlesPostPage(models.Model):
title_post = models.OneToOneField(TitlePost)
hello = models.CharField(max_length=100, unique=True)
def __unicode__(self):
return self.hello
Why don't TopTitlesPostPage and NewTitlesPostPage inherit all the attributes from TitlePost? For instance, if I try to call the likes in my template using TopTitlesPostPage, it will not execute because the likes attribute is not inherited. Does OneToOneField have something to do with the problem? I did read that making TitlePost a meta class will help but I need it to have a table in my database. I actually want all of them to have a table in my data base. Then again, maybe I am approaching this the wrong way and I should use just TitlePost as a model to generate everything?
The behaviour you would like to see is called multi table inheritance. Every child class internally ends up with the same thing that you wrote, so with a one to one field to the base class TitlePost, but it's internally managed by django.
If you do multiple inheritance like the code below you will be able to write:
k=TopTitlesPostPage.objects.create(hello="Hello",title_name="Heh")
That means the fields will be directly accessible.
from django.db import models
class TitlePost(models.Model):
title_name = models.CharField(max_length=100, unique=True)
title_body = models.TextField(max_length=30000)
title_why = models.TextField(max_length=250, null=True)
title_publication_date = models.DateTimeField('date')
likes = models.IntegerField(default=0)
dislikes = models.IntegerField(default=0)
def __unicode__(self):
return self.title_name
class TopTitlesPostPage(TitlePost):
hello = models.CharField(max_length=100, unique=True)
def __unicode__(self):
return self.hello
class NewTitlesPostPage(TitlePost):
hello = models.CharField(max_length=100, unique=True)
def __unicode__(self):
return self.hello
In case you are never actually going to reference the base class TitlePost, but only its children it might be more appropriate to make `TitlePost abstract:
class TitlePost(models.Model):
title_name = models.CharField(max_length=100, unique=True)
title_body = models.TextField(max_length=30000)
title_why = models.TextField(max_length=250, null=True)
title_publication_date = models.DateTimeField('date')
likes = models.IntegerField(default=0)
dislikes = models.IntegerField(default=0)
class Meta:
abstract = True
def __unicode__(self):
return self.title_name
Making TitlePostabstract will omit the creation of the table TitlePostin the database, and the child models will end up with the fields of the base class inserted into their own tables seperately. If the base class is just for factoring out common functionality this is the preferred way to go.
For huge queries this will also make a difference in performance because the ORM will need to do less JOINoperations.
It's not possible to install Foreign Keys to abstract models in Django.
You can however install Foreign Keys to a non abstract base class. The only limitation is that the reverse Foreign Key relation will return the base class instances.
You can circumvent this limitation by using django-polymorphic.
Django Polymorphic allows you to query the base class objects but retrieves the child class instances:
>>> Project.objects.create(topic="Department Party")
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
>>> Project.objects.all()
[ <Project: id 1, topic "Department Party">,
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
To use django polymorphic you only need to declare your models with Polymorphic Model as base class:
from django.db import models
from polymorphic import PolymorphicModel
class ModelA(PolymorphicModel):
field1 = models.CharField(max_length=10)
class ModelB(ModelA):
field2 = models.CharField(max_length=10)
class ModelC(ModelB):
field3 = models.CharField(max_length=10)
Foreign keys will also return the child class instances, which is really cool if you're trying to be polymorphic.
# The model holding the relation may be any kind of model, polymorphic or not
class RelatingModel(models.Model):
many2many = models.ManyToManyField('ModelA') # ManyToMany relation to a polymorphic model
>>> o=RelatingModel.objects.create()
>>> o.many2many.add(ModelA.objects.get(id=1))
>>> o.many2many.add(ModelB.objects.get(id=2))
>>> o.many2many.add(ModelC.objects.get(id=3))
>>> o.many2many.all()
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Take into account that these queries will be slightly less performant.
U need to extend the classes like follows:
class TopTitlesPostPage(TitlePost):
U can add more and inherit from multiple models just by mentionin g all the models comma separated! This all the fields from the models will be created in the child class as well
EDIT:
The way i would do it is to create an Abstract class which contains all your common fields and extend it into your TitlePost, TopTitlesPostPagea and NewTitlesPostPage
You need to have TopTitlesPostPage and NewTitlesPostPage extend the base class of TitlePost like so ...
class TopTitlesPostPage(models.Model)
You don't need a OneToOneField if you are inheriting from the base class, since the attributes of TitlePost will be available to you in the subclass. If you want to make TitlePost abstract (you can not declare an instance of that class, only inherit from it) you have to add it to the meta class
class TitlePost(models.Model):
class Meta:
abstract = True
Here is a link to the documentation.

Editing property of foreignkey object

I am trying to access a ForeignKey object in Django, but only manage to show its values, not to edit them.
class ShippingAddress(models.Model):
name = models.CharField(max_length=63, primary_key=True)
street = models.CharField(max_length=63)
houseNumber = models.CharField(max_length=10)
zipCode = models.CharField(max_length=10)
city = models.CharField(max_length=63)
country = models.CharField(max_length=63)
class MainClass(models.Model):
name = models.CharField(max_length=63)
creationDate = models.DateTimeField(blank=True)
aShippingAddress = models.ForeignKey(ShippingAddress)
this would be an example. I would now like to make the values of the ShippingAddress model directly accesible and editable within the main class. Right now though I manage to only access the object itself, not every value of it directly.
(admin.py)
def editZipCode(self, obj):
return obj.aShippingAddress.zipCode
this way I manage to show each value at least, but that's it.
Any ideas are welcome.
You need to use an inline:
class MainClassInline(admin.TabularInline):
model = MainClass
class ShippingAddressAdmin(admin.ModelAdmin):
...
inlines = (MainClassInline,)

Categories

Resources