Model in sub-directory via app_label? - django

In order to place my models in sub-folders I tried to use the app_label Meta field as described here.
My directory structure looks like this:
project
apps
foo
models
__init__.py
bar_model.py
In bar_model.py I define my Model like this:
from django.db import models
class SomeModel(models.Model):
field = models.TextField()
class Meta:
app_label = "foo"
I can successfully import the model like so:
from apps.foo.models.bar_model import SomeModel
However, running:
./manage.py syncdb
does not create the table for the model. In verbose mode I do see, however, that the app "foo" is properly recognized (it's in INSTALLED_APPS in settings.py). Moving the model to models.py under foo does work.
Is there some specific convention not documented with app_label or with the whole mechanism that prevents this model structure from being recognized by syncdb?

See Django ticket #10985: Explain how models can be organised in a directory
It may be that you aren't importing your models into __init__.py?

syncdb will not create tables for models not located in <appname>.models, so import it in there, e.g. from apps.foo.models import SomeModel.

Here is a solution if you have a newer version of Django, supposing you have a subfolder named subfolder :
in apps.py of your folder app:
from django.apps import AppConfig
class MyappConfig(AppConfig):
name = 'myapp'
def ready(self):
from myapp.subfolder import models

Related

Difference between models and Model in django

What exactly is the difference between models and Model.while writing a model in django.
from django.db import models
class Teacher(models.Model):
In your example, Model is the class from which you are inheriting, and models is where that Model class is located.
The models import is a folder, which implicitly runs the directory:
django/db/models/__init__.py
models.Model is a class found in the models folder within base.py. This is because __init__.py in the models folder imports base.py as well.

How to register Django models that are only used in tests

I'm writing an app that has a custom model field. I want to test that field, using a model that is only used within tests, but I'm struggling to get the model working in tests.
Some of my app structure is like this:
myapp/
fields.py
models.py
tests/
fields/
models.py
tests.py
settings.py
test_models.py
test_views.py
tests/fields/models.py is like this:
from django.db import models
from myapp.fields import MyCustomField
class MyTestModel(models.Model):
custom_field = MyCustomField()
I then import MyTestModel and try to use it in tests/fields/tests.py.
Things I've tried...
1. Initially I got:
RuntimeError: Model class tests.fields.models.MyTestModel doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
2. So I added "tests", to INSTALLED_APPS in tests/settings.py. But then I get:
django.db.utils.OperationalError: no such table: tests_mytestmodel
3. I've seen this in the docs about isolating model registration in tests. So I've tried moving the models into tests/fields/tests.py:
from django.db import models
from django.test import TestCase
from django.test.utils import isolate_apps
from myapp.fields import MyCustomField
#isolate_apps('tests')
class MyTestCase(TestCase):
def test_things_work(self):
class MyTestModel(models.Model):
custom_field = MyCustomField()
obj = MyTestModel.objects.create(custom_field='foo')
self.assertEqual(...)
But that still gets me:
django.db.utils.OperationalError: no such table: tests_mytestmodel
I've tried looking through the tests of lots of other Django projects that provide custom fields and can't see what I'm missing that (apparently) makes their tests with models work.
It sounds like, somehow, there need to be migrations for these models that are only used in the tests... is that right? How? Or something else?
Since your test app is in a nested folder you need to specify the whole path to it in INSTALLED_APPS instead of the parent folder, like:
INSTALLED_APPS = [
...,
"tests.fields"
]

South not detecting new model file in model folder

I'm working with an existing django project that uses south. Within each app there's a models folder where models are stored in different files. I have added a new file (shown below) but when I attempt to create migration files for the model, South fails to detect the new file and says: "Nothing seems to have changed." My question is what is the correct way to get south to detect this new model? Thanks.
from django.contrib.auth.models import User, Group
from django.db import models
from django.contrib import admin
class AdgroupEmailRecipients(models.model):
users = models.ForeignKey(User)
class Meta:
app_label = 'wifipromo'
class AdgroupEmailRecipientsAdmin(admin.ModelAdmin):
list_display = ('user_first_name', 'user_last_name', 'user_email')
def user_first_name(self, obj):
return obj.users.first_name
user_first_name.short_description = "First Name"
def user_last_name(self, obj):
return obj.users.last_name
user_last_name.short_description = "Last Name"
def user_email(self, obj):
return obj.users.email
user_email.short_description = "Email"
In the __init__.py file of the models folder, you have to import the model for South or even syncdb to detect it. Basically django is just looking for one file with all your models... and if you import it all in init.py that's what the system will see.

Django: How to create a model from a definition in another module

Lets say I have this Django setup:
appA - models.py
- views.py
- etc...
global - models.py
What I want is import the models from global.models in appA.models so that they are treated as a normal appA models by syncdb and south.
global.models:
from django.db import models
class Foo(models.Model):
#django model stuff
appA.models:
try 1:
from global.models import *
>>manage.py schemamigration appA --auto
>>does not see Foo
try 2:
from global.models import Foo
class Foo(Foo):
pass
>>manage.py schemamigration appA --auto
>>Error: One or more models did not validate:
>>appA.Foo: 'foo_ptr' has a relation with model <class 'global.models.Foo'>, which has either not been installed or is abstract.
What is the correct way to accomplish this?
from .models import Foo
class Foo(models.Model):
#stuff
class Meta:
abstract = True
global is a python statement, you should not use it to name one of your package. Try using a different name, and don't forget to put a __init__.py file in the directory to turn it into a python package.

Django Models not Showing Up in DB after syncdb

I have a models folder that has a few models in files that are already in the DB. I have just added another file/model but it is not being added to the DB when I run syncdb. I've tried manage.py validate and it is running fine. I have also run the code and it only fails when it tries to save with "table does not exist".
the original structure was like this:
/models
-- __init__.py
-- file1.py
-- file2.py
and __init__.py looked like:
from file1 import File1Model
from file2 import File2Model
I added file3.py
/models
-- __init__.py
-- file1.py
-- file2.py
-- file3.py
and modified __init__.py
from file1 import File1Model
from file2 import File2Model
from file3 import File3Model
And the contents of file3 (names may have been changed to protect the innocent, but besides that its the exact file):
UPDATE: just tried adding a primary key since the id field may have been messing with the automatically added integer primary key id. Also tried a few variations but no dice.
from django.db import models
from django.contrib.auth.models import User
class File3Model(models.Model):
user = models.OneToOneField(User)
token = models.CharField(max_length=255, blank=False, null=False)
id = models.CharField(primary_key=True, max_length=255)
class Admin:
pass
class Meta:
app_label = 'coolabel'
def __unicode__(self):
return self.user.username
#staticmethod
def getinstance(user, token, id):
try:
instance = File3Model.objects.get(pk=id)
if instance.token != token:
instance.token = token
instance.save()
return instance
except:
pass
instance = File3Model()
instance.user = user
instance.token = token
instance.id = id
instance.save()
return instance
So in this example, File1Model and File2Model are already in the DB and remain in the DB after syncdb. However, File3Model is not added even after rerunning syncdb. Is there any way to figure out why the new model isn't being added??
If you define the model outside of models.py, you have to set the app_label attribute on the models Meta class.
Edit: The app_label has to refer to an app in your INSTALLED_APPS setting. It should probably match the name of the app that the models directory is in, unless you've got a really good reason to do otherwise. That seems to have been your problem here.
class File3Model(models.Model):
foo = models.CharField(...)
...
class Meta:
app_label = "my_app"
Note that syncdb will never remove any tables from the db. The other tables were probably created with syncdb before the models.py was replaced with the directory structure.
set app_label to my app solves my problem.
Why did you split you models and having models folder instead of placing models in models.py ?
In my own project there are about 10 models live in models.py and I'm fine with it.
You can also try manage.py syncdb --all.
And I think its better to keep all models in one single file and import them like from my_app.models import model_name instead of keeping in mind to import necessary model into models/__init__.py. By the way you avoid many problems and long lined imports and don't care about where some_model lives among models/*.py files.
Thanks,
Sultan
BOOM!
I was using a different app_label for the new model but it has to be the same across the model group.
The other models labels were "mediocrelabel" and my new model had the label "coolabel". I changed the new model's label to "mediocrelabel" and now they are being added to the DB correctly.
Thanks for your help, folks!