Avoiding circular imports in Django Models (Config

2019-07-13 08:01发布

I've created a Configuration model in django so that the site admin can change some settings on the fly, however some of the models are reliant on these configurations. I'm using Django 2.0.2 and Python 3.6.4.

I created a config.py file in the same directory as models.py.

Let me paracode (paraphase the code? Real Enum has many more options):

# models.py
from .config import *

class Configuration(models.Model):
    starting_money = models.IntegerField(default=1000)

class Person(models.Model):
    funds = models.IntegarField(default=getConfig(ConfigData.STARTING_MONEY))

# config.py
from .models import Configuration

class ConfigData(Enum):
    STARTING_MONEY = 1
def getConfig(data):
    if not isinstance(data, ConfigData):
        raise TypeError(f"{data} is not a valid configuration type")
    try:
        config = Configuration.objects.get_or_create()
    except Configuration.MultipleObjectsReturned:
        # Cleans database in case multiple configurations exist.
        Configuration.objects.exclude(Configuration.objects.first()).delete()
        return getConfig(data)
    if data is ConfigData.MAXIMUM_STAKE:
        return config.max_stake

How can I do this without an import error? I've tried absolute imports

2条回答
小情绪 Triste *
2楼-- · 2019-07-13 08:44

Willem Van Onsem's solution is a good one. I have a different approach which I have used for circular model dependencies using django's Applications registry. I post it here as an alternate solution, in part because I'd like feedback from more experienced python coders as to whether or not there are problems with this approach.

In a utility module, define the following method:

from django.apps import apps as django_apps

def model_by_name(app_name, model_name):
  return django_apps.get_app_config(app_name).get_model(model_name)

Then in your getConfig, omit the import and replace the line

config = Configuration.objects.get_or_create()

with the following:

config_class = model_by_name(APP_NAME, 'Configuration')
config = config_class.objects.get_or_create()
查看更多
可以哭但决不认输i
3楼-- · 2019-07-13 08:49

You can postpone loading the models.py by loading it in the getConfig(data) function, as a result we no longer need models.py at the time we load config.py:

# config.py (no import in the head)
class ConfigData(Enum):
    STARTING_MONEY = 1

def getConfig(data):
    from .models import Configuration
    if not isinstance(data, ConfigData):
        raise TypeError(f"{data} is not a valid configuration type")
    try:
        config = Configuration.objects.get_or_create()
    except Configuration.MultipleObjectsReturned:
        # Cleans database in case multiple configurations exist.
        Configuration.objects.exclude(Configuration.objects.first()).delete()
        return getConfig(data)
    if data is ConfigData.MAXIMUM_STAKE:
        return config.max_stake

We thus do not load models.py in the config.py. We only check if it is loaded (and load it if not) when we actually execute the getConfig function, which is later in the process.

查看更多
登录 后发表回答