Dynamically populate the choices of a CharField with the fields of some other models: Models aren't loaded yet RegistryError

Good day everyone, hope you’re doing really well.

Straight to the point, let’s say we have an app with some models:

from django.apps import apps
    from django.db import models

    class A(models.Model):
        # A lot of fields go here
    
    class B(models.Model):
        # A lot of fields go here
    
    # And some more models ...
    
    class ModelChoices(models.TextChoices):
        A = 'A'
        B = 'B'
        ...
    
    class Filterset(models.Model):
        model = models.CharField(max_length=32, choices=ModelChoices.choices)
        attribute = models.CharField(max_length=32,  
    choices=auto_generate_attributes().choices)
    
        def auto_generate_attributes():

            all_attributes = set()
            for model_name in ModelChoices.values:
                ModelClass = apps.get_model(model_name)
                model_fields = ModelClass._meta.get_fields()
                model_attributes = [(attr.name, attr.name) for attr in model_fields]
                all_attributes.update(model_attributes),
            Attributes = models.TextChoices(
                'Attributes',
                list(all_attributes),
            )
            return Attributes

Now you’re probably wondering what’s the point of this table. In short, I’ll use it in a M2M relationship with another model so that an User can filter on values of different models at once and return a list of FK objects that all models share (Sounds abstract, but trust me). But the other models are still a work in progress and will edit/create new fields as time goes, that’s why I have to generate them dynamically instead of hard-coding them.

The issue is trying to get the models classes at initilization time to popuplate the choices of the Filterset model, because Django returns this error:

# ...Rest of the tracestack goes here...
ModelClass = apps.get_model(model_name)
  File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 19
8, in get_model
    self.check_models_ready()
  File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 14
0, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

I’ve tried many of the solutions proposed in other forums of the same nature:

  1. Importing the models explicitly instead of using get_model , returns the same error.
  2. Using import django followed by django.setup() at the top of the file, returns RuntimeError: populate() isnt reentrant , which doesn’t say much.
  3. Creating the attributes list in another file outside of models, returns the same error whenever you call the function.

I’ve seen people try to overwrite the ready() method of the AppConfig class for the app, but don’t know how to translate that into my specific problem. Is there really a way to have this successfully generate the fields at initialization and create the migration with all choices?

Thank you in advance for any help you may have. I’ve thoroughly searched the internet but no luck still, so I feel as helpless as I can be.

To clarify, your root question is that you wish to create a list.
This list is to contain all the fields for every model within a list of models.
Is this correct?

If so, I’d probably do this with a management command and store that list in a table rather than going through this process every time you’re preparing a form. Then it becomes just another step in your deployment process, similar to migrate and collectstatic.

Very well put, you’ve summarized clearly what I wanted to do.

If truly there’s no way to do this with the redy() method, I would very closely follow your advice and have it be an additional command on my deployment routine.

Now the reason I was adamant on generating the list of fields inside my other model is that I was planning on also adding constraints to the model, in such a way that an attribute value must always be belonging to the model value, that is, in a Q object something like: Q(model__exact=model_name, attribute__in=attributes_of_that_model). Can I generate the list in side the Meta Class that houses constraints of the Filterset model?

Or does something like that would be equally impossible to do?

I didn’t say there wasn’t a way to do it there, only that there’s no benefit to doing it there.

There are a couple ways this can be done, in some ways it’s dependent upon how the users are entering the model name / attribute name in the form - or how you’re handling that in the view.

For example, if you’re using select fields, you could have two select fields - the first for selecting a model, the second being chained to the first get populated with the attributes of the selected model.

Or, you could do it as a single select field, where the choice is shown (perhaps) in the format "ModelName : AttributeName " and they have to scroll down to the right combination.

More detail about the intended UI and model use would allow for more specific and targeted advice.

I’m afraid I can’t tell you much more of use, since I’m using Django Rest Framework and don’t really interact with Forms at all. That’s why I was counting on doing it at the model level. Although your first example is exactly how I would want it to show in an UI.

So basically, the model use is giving a user the ability to choose multiple filters that can chain together in different models and return a queryset of some objects that all my models share in common (A, B, C, etc. have a FK to model Z, then Filterset chain its model, attributes and value of attr fields to return a queryset of Z objects found in the A,B,C, etc., models that fulfill the filters). This is done in another model that has M2M relationship with Filterset.

It doesn’t matter much how the data is formatted upon submission, your view is going to (needs to) be able to perform the necessary validation of the request submitted.

What I would suggest for right now is to ignore what that data is, or what it represents. You’ve got three separate issues that can each be handled independently of the others:

  • Creating the valid options for input. (That part of this that I suggested may be best handled in a custom management command.)
  • Validating the input submitted from the client.
  • Using the submitted data for whatever business purposes desired.

Whatever constraints you need to apply to the submitted data can be handled by your view that receives the post request. It is, however, a separate logical step from using that data for its intended purposes.

Thank you very much for the detailed response.

I can say with 100% certainty that you’ve described the logical steps I had in mind very clearly. Thanks to your suggestions eariler, I have completed step 1 more or less successfully and I actually had the last step( using the submitted data) written before hand. The last obstacle is the middle step, and as you said, it has do with the validation of the data. From my limited knowledge, I had the notion that you could only validate from the constraints at model level. is there any other similar approach that would work?

You have full access to the submitted data in the view. You can apply whatever logic you want in the view to perform whatever validation is required. There’s no limitation that says you can only validate in the model.

The idea of having the constraints in the model is that you’re sure to apply those constraints from any location where your code is working with a model. However, that doesn’t appear to be the case here.

I’ve gotten the impression that the data being submitted is not itself being saved, rather it’s being used to build a query. So from that perspective, there’s really no model involved in the submission. In that case, the view is the logical place to perform the validation.

Thank you so much for your help, dear fellow. You’ve cleared all my doubts and have cleared a path that I agree it’s the best. I wish you the best and for life to treat you fair.