Django migrations don't work if you use the model in code without making migrations first
smark-1 opened this issue · 6 comments
if I take my existing model
class SiteConfiguration(SingletonModel):
site_name = models.CharField(max_length=255, default='Administration')
then add to it a additional field
class SiteConfiguration(SingletonModel):
site_name = models.CharField(max_length=255, default='Administration')
message = models.TextField(default="this is a standard message")
then inside of django views.py
...
site_config = SiteConfiguration.get_solo()
def view(request):
site_config.message
return HttpResponse("some response")
...
then try to run a migration i get a error
django.db.utils.OperationalError: no such column: siteconfiguration.message
this is a very big problem for me because I often write my code first to see how it looks first before making the migrations. Also this does work in a standard model so it should work in a SingletonModel
@dragoncommits I might be late to the party, but the problem is how you use it.
You are using SiteConfiguration.get_solo()
(which does a DB/ORM call) in the base of file/module views.py
and when Django is getting initialised, it will do that call but will fail because it's not yet ready.
This is a general problem with code that gets run at import time (an example: https://stackoverflow.com/questions/43326132/how-to-avoid-import-time-database-access-in-django).
What I would suggest to do is create a function with lru_cache
that returns the configuration:
from functools import lru_cache
@lru_cache(maxsize=1)
def get_config():
return SiteConfiguration.get_solo()
...
def view(request):
site_config = get_config()
site_config.message
return HttpResponse("some response")
...
Hi,
Just a little improvement:
class SiteConfiguration(SingletonModel):
site_name = models.CharField(max_length=255, default='Site Name')
maintenance_mode = models.BooleanField(default=False)
def __str__(self):
return "Site configuration"
@classmethod
@lru_cache(maxsize=1)
def get_solo(cls):
return super().get_solo()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.get_solo.cache_clear()
class Meta:
verbose_name = "Site configuration"
Like this, it is completely transparent.