1

I have a function make_fields_permissions that I need to use it inside the model calss in order to parse the fields and to make permissions for each field like [('can_view_first_name_field','can view first name'),...]

  • goal I need to call and override Person class and inside it self
  • I tried

def __init__(self,*args, **kwargs):
    self.Meta.permissions = make_fields_permissions(self.model)
    super().__init__(*args, **kwargs)

  • My code look like this
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

    def __init__(self, *args, **kwargs):
        # kwargs['user']['permissions'] =  make_fields_permissions(Profile)
        # x = self['age']
        super().__init__(*args, **kwargs)


    class Meta:
        permissions = make_fields_permissions(Person) #< I can't use the same model inside meta

Ali Husham
  • 816
  • 10
  • 31
  • 1
    You can impelement the logic to *create* `Permission` objects: https://docs.djangoproject.com/en/3.2/ref/contrib/auth/#django.contrib.auth.models.Permission so instead of using these in the `Meta`, you make a function that inspects the field of a given model, and constructs `Permission` objects. – Willem Van Onsem Jun 06 '21 at 10:49
  • you mean outside the class i have to say `Person.permissions = myFunction(Person)`? – Ali Husham Jun 06 '21 at 10:52
  • 2
    Or you can decide to dabble in metaclasses (Which can customize how classes are created) and try inheriting from `ModelBase`. Although this would be very easily breakable as the `ModelBase` (which is the metaclass used by `Model`) already does plenty of things so such modification would need to be very _careful_, it would be simply easier for you to do this in the app configs `ready` method and try to make the permissions there by looping over the models fields. – Abdul Aziz Barkat Jun 06 '21 at 10:53
  • 2
    @alial-karaawi: no, just `myFunction(Person)`, and there you *crete* the permissions. You can trigger this for example in the `ready` method of the app config, like Abdul says. – Willem Van Onsem Jun 06 '21 at 11:07
  • @AbdulAzizBarkat can you write it in the answers please I don't understand it like this? – Ali Husham Jun 08 '21 at 07:27

1 Answers1

1

Your goal is as follows:

  • Goal X (Real goal): Create permissions dynamically according to the model fields
  • Goal Y (Perceived goal that will achieve X): Call the model class while creating it.

Note: See What is the XY problem?

Let us first discuss Goal Y and why it is too complex, and somewhat unfeasable. When one wants to customize how the creation of a class occurs one would use metaclasses, and at first sight this would appear as a perfect solution for your needs (in fact if you do create one properly it would be). But the problem here is that Model already has a metaclass being ModelBase and it is already doing lots of stuff and is a little complicated. If we would want a custom metaclass we would need to inherit from it and very carefully work around its implementation to do what we want. Furthermore making it would not be the end of the story, because then we would need to maintain it since it would be easily breakable by updates to Django. Hence Goal Y is not feasible.

Moving on to the actual Goal X to do that one can Programmatically create permissions [Django docs]. A good place to do this would be in the app configs ready method. For all apps created using startapp there is an apps.py file which has an appconfig inheriting from AppConfig, when the models are loaded its ready method is called. Hence this method is used to do various tasks like attaching signals, various setup like tasks, etc. Modify the appconfig of your app to create permissions programmatically like so:

from django.apps import AppConfig


class YourAppConfig(AppConfig):
    default_auto_field = 'django.db.models.AutoField' # Don't modify, keep it as it is in your code
    name = 'your_app' # Don't modify, keep it as it is in your code
    
    def ready(self):
        from django.contrib.auth.models import Permission
        from django.contrib.contenttypes.models import ContentType
        from path.to import make_fields_permissions
        from .models import Person
        # import models here, not outside as models may not be loaded yet
        content_type = ContentType.objects.get_for_model(Person)
        for codename, name in make_fields_permissions(Person):
            Permission.objects.get_or_create(
                codename=codename, # 'can_view_first_name_field'
                name=name, # 'can view first name'
                content_type=content_type,
            )
Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33