Django permission inheritance - django

I'm trying to inherit from a class which has some permissions defined in it's Meta class, such as this:-
class MyModel(models.Model):
name = models.CharField(max_length=100)
class Meta:
permissions = (
('view_mymodel', 'Can view mymodel'),
)
but when I create a model that inherits from it like this:-
class MySubModel(MyModel):
pass
it doesn't inherit the permissions of the superclass.
Why is this and what is the best way to solve this problem without changing the MyModel class?

Related

If an app has multiple models with the same field, whats the best practice for keeping things DRY?

For example, if I have 3 models that look like this:
class CallLog(models.Model):
lead_id = models.BigIntegerField("Lead ID")
# other fields
class EmailLog(models.Model):
lead_id = models.BigIntegerField("Lead ID")
# other fields
class TextLog(models.Model):
lead_id = models.BigIntegerField("Lead ID")
# other fields
Do I add lead_id to each model individually or is there a way to only type it once?
Yes, you can define an abstract base class [Django-doc]:
class LeadId(models.Model):
lead_id = models.BigIntegerField("Lead ID")
class Meta:
abstract = True
and then inherit this in the other models:
class CallLog(LeadId, models.Model):
# other fields…
class EmailLog(LeadId, models.Model):
# other fields…
class TextLog(LeadId, models.Model):
# other fields…
You can define multiple such abstract base classes, and use multiple inheritance such that models inherit from multiple of such classes.

How to create an admin mixin for common/abstract model in Django

I am creating a multi tenant app and have the following abstract class that all relevant tenant specific models inherit from:
class TenantAwareModel(models.Model):
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)
class Meta:
abstract = True
When registering models in the Django admin, I want to keep my code DRY and not have to add 'tenant' to every single search_fields, list_display, list_filter etc so I tried creating a super class to inherit from.
class TenantAwareAdmin(admin.ModelAdmin):
class Meta:
abstract = True
# change_form_template = "admin/professional_enquiry_change_form.html"
search_fields = ('tenant__id', 'tenant__name', 'tenant__domain',)
list_display = ('tenant',)
list_filter = ('tenant',)
And then trying to inherit that in the registration of the other models. E.g.
class UserAdmin(TenantAwareAdmin, admin.ModelAdmin ):
...
This approach matches the way the TenantAwareModel works.
class TenantAwareModel(models.Model):
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)
class Meta:
abstract = True
class User(AbstractUser, Uuidable, Timestampable, TenantAwareModel):
...
and it is of course giving me:
django.core.exceptions.ImproperlyConfigured: The model TenantAwareModel is abstract, so it cannot be registered with admin.
Does anybody know the proper way to do this to avoid all the duplicated code?
The Meta class and abstract=True can only be defined in the model definition, not in the ModelAdmin.
The TenantAwareModel is an abstract model that allows you to avoid repeating the common fields in the definition of others models, but you can't register a abstract model in the admin site. The abstract model is not related with any table, its only task is to be a base from which inherit in other models that will represent tables(not abstract models) and which will be able to be registered in the admin site.
To avoid repeat the same fields in search_fields, list_display, etc, you should create a mixin like this:
class TenantAwareAdminMixin:
search_fields = [..., ...]
list_display = [..., ...]
...
and inherit from this
class UserAdmin(TenantAwareAdminMixin, admin.ModelAdmin):
...
But there is a problem... If you override some field defined in the TenantAwareAdminMixin, this will overwrite the value provided by the mixin forcing you to explicitly add the inherited values plus those new values, like this:
class UserAdmin(TenantAwareAdminMixin, admin.ModelAdmin):
list_display = TenantAwareAdminMixin.list_display + ['new_value']
...
It is possible to obtain a similar result by overriding the get_list_display, etc. methods.

How to show fields of inherited model in django?

class BaseModel(models.Model): # base class should subclass 'django.db.models.Model'
creation_date = models.DateTimeField(..) # define the common field1
class Meta:
abstract=True # Set this model as Abstract
Inherit this Base class in models
After creating the abstract base class BaseModel, I inherited this class in my models.
class MyModel1(BaseModel): # inherit the base model class
# define other non-common fields here
...
After creating an object of class 'MyModel1', I want the 'creation_date' field to be shown in admin interface.
So that I can see the datetime when an object of class 'MyModel1' is created.
Solution:
class MyModel1Admin(admin.ModelAdmin):
readonly_fields= ['creation_date',]
admin.site.register(MyModel1,MyModel1Admin)

How can I add automatically CRUD permissions to a django model inside the Meta class?

Imagine I had the following model
class Post(models.Model):
name = models.CharField(max_length=20)
body = models.TextField()
class Meta:
permissions = permission_generator()
and what I want is that the permission_generator() to generate the tuple
(
('create_pet', 'create post'),
('read_pet', 'read post'),
('update_pet', 'update post'),
('delete_post', 'delete post'),
)
In a way that I could reuse in all my models without repeating myself each time I define a new model.
how can something like this be implemented?
I've just found out that django adds by default
add_modelname
change_modelname
delete_modelname
https://docs.djangoproject.com/en/dev/topics/auth/default/#default-permissions
You can create abstract model and inherit it. For example:
class BaseModel(models.Model):
class Meta:
abstract=True
permissions = (
('create', 'Create'),
('read', 'Read'),
('update', 'Update'),
('delete', 'Delete')
)
class Cat(BaseModel):
name = models.CharField()
When you inherit that model and do data migration these permissions will be added to django_permissions table and will look like:
app | cat | Permission name

Django Multi-table inheritance: specify custom one-to-one column name

I'm trying to create a Django ORM mapping that's compatible with an existing data model, so I'm trying to work with an existing set of table and column names.
I've got a multi-table inheritance situation where a class InformationObject derives from class Object. I'd like to let Django handle this the usual way:
class Object(models.Model):
class Meta:
db_table = "object"
class InformationObject(Object):
class Meta:
db_table = "information_object"
In this case Django would automatically create a one-to-one field on the inheriting model called object_ptr_id. However, on the schema I'm constrained to use, the reference to the Object is simply called "id". So:
Is there a way to somehow specify the name of the column Django auto-magically uses for multi-table inheritance?
The alternative, which I'll have to use otherwise, is to use an explicit one-to-one field, but then I won't be able to inherit non-database methods from the Object model:
class Object(models.Model):
class Meta:
db_table = "object"
class InformationObject(models.Model):
class Meta:
db_table = "information_object"
id = models.OneToOneField(Object, primary_key=True, db_column="id")
Any ideas? Maybe I could create a common base class for both of them and put non-db methods there...?
From the django docs (development version):
As mentioned, Django will automatically create a OneToOneField linking your child class back any non-abstract parent models. If you want to control the name of the attribute linking back to the parent, you can create your own OneToOneField and set parent_link=True to indicate that your field is the link back to the parent class.
As mentioned by #fusion quoting from the docs, you will have to create a OneToOneField if you want to specify the column, while using model inheritance. The inherited fields will be available in the child class in both self scope AND the one-to-one field.
class Object(models.Model):
class Meta:
db_table = "object"
column_1 = models.CharField()
class InformationObject(Object):
class Meta:
db_table = "information_object"
# arbitrary property name (parent_link)
parent_link = models.OneToOneField(Object, primary_key=True, db_column="id", parent_link=True)
In this example:
>>> inf_obj = InformationObject.objects.get(pk=1)
>>> print inf_obj.column_1 == inf_obj.parent_link.column_1
True