Currently all my models are in models.py. Ist becomming very messy.
Can i have the separate file like base_models.py so that i put my main models there which i don't want to touch
Also same case for views and put in separate folder rather than develop a new app
Yes, it's doable. It's not particularly pretty though:
make models a module, so your directory structure looks like this:
- models
|- __init__.py
|- some_model.py
|- some_other_model.py
|- ...
now, the magic lies in __init__.py and some little extras in the models. __init__.py:
from some_model import SomeModel
from some_other_model import SomeOtherModel
__all__ = [
'SomeModel',
'SomeOtherModel',
]
some_model.py:
class SomeModel(models.Model):
class Meta(object):
app_label = 'yourapplabel'
db_table = 'yourapplabel_somemodel'
Everything acjohnson55 said, plus the fact that you need to specify the app_label attribute in each model's Meta class.
A link to an actual example on github:
https://github.com/stefanfoulis/django-filer/tree/develop/filer/models
you can separate the model file like this :
-------models/
-------------- init.py
-------------- usermodels.py
--------------othermodel.py
in the init.py:
---------------from usermodels import *
---------------from othermodel import *
and in the *models.py , add META class:
--------class Meta:
--------------app_label = 'appName'
Yes, just make a folder called models and in that folder put all of your separated model files. You need to put a file called __init__.py in the models folder for it to be considered the models module. In __init__.py, use from ... import ... to put the names you want available directly in yourapp.models, otherwise you will have to import them as yourapp.models.base_model, or whatever submodule name you use.
Also, in each model, you will have to add a Meta attribute called app_label = 'yourapp' to make sure your models are recognized as part of the app.
This is how I normally do it:
# Folder structure:
#==================
# models/
# __init__.py
# products.py
# stocks.py
# In init.py (don't forget the period before model file name)
#===========
from .products import Product
from .stocks import Stock
__all__ = [
'Product',
'Stock',
]
# And run "py manage.py makemigrations" and "py manage.py migrate" as normal
Related
I have created an app called base and added a new model to it. However, whenever I run python3 manage.py makemigrations base It says no changes detected.
I have already added my in installed apps
INSTALLED_APPS = [
...
'base.apps.BaseConfig',
...
]
And it does have the migrations folder inside the app containing and __init__.py file. I was wondering what is causing this issue.
This is my model:
from django.db import models
# Create your models here.
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now=True)
modified_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
And my settings contains 4 files:
settings:
--__init__.py
-- dev.py
-- prod.py
-- staging.py
-- common.py
My __init__.py:
import os
from .common import *
from decouple import config
environment = config('DEV_ENV', cast=str)
if environment == "dev":
from .dev import *
elif environment == "staging":
from .staging import *
elif environment == "prod":
from .prod import *
I have also already tried running
python3 manage.py makemigration base --settings=project.settings.dev and it still says
no changes detected
From the Abstract base classes doc,
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class. This model will then not be used to create any database table. Instead, when it is used as a base class for other models, its fields will be added to those of the child class.
That is, if you remove abstract = True or set abstract = False, the Django will generate migration files for you!!!
after customizing django oscar app (address) and adding a new field named 'area' when I run migrate it gave me Unknown field(s) (area) specified for UserAddress
I used the command
./manage.py oscar_fork_app address myappsfolder/
after creating the folder and __init__.py file in it
then I started to customize the app like this:
#myappsfolder/address/models.py
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from oscar.apps.address.abstract_models import AbstractAddress
class Address(AbstractAddress):
area = models.CharField(_("Area"), max_length=120,
choices=settings.AREA_CHOICES, blank=True)
from oscar.apps.address.models import *
#myappsfolder/address/forms.py
from oscar.apps.address import forms as base_forms
class UserAddressForm(base_forms.UserAddressForm):
class Meta(base_forms.UserAddressForm.Meta):
fields = ['area']
I didn't touch the admin.py , config.py and __init__.py that have been created by the command ./manage.py oscar_fork_app address myappsfolder/
also the __init__.py that I created in myappsfolder is empty, should I add something to these files ?
What should I do to customize this app ? or any other app ?
If I edited the models in oscar/apps/address/abstract_models.py it only apply in local host and the error in forms disappear , which means that django still reading the models from oscar/apps not from myappsfolder.
You cannot override AbstractAddress in that way because the changes will not propagate to other models such as UserAddress that directly subclass AbstractAddress. Instead you need to override the specific address models that you want to change. So if you want to change UserAddress then override that particular model:
from oscar.apps.address.abstract_models import AbstractUserAddress
class UserAddress(AbstractUserAddress):
area = models.CharField(_("Area"), max_length=120, choices=settings.AREA_CHOICES, blank=True)
... after which your changes to UserAddressForm should work.
If you need to override ShippingAddress as well, then you will also have to fork the shipping app and do the same thing there, because that is where the model lives.
My old structure used to be like this:
models.py
class Model1
class Model2
...
I have since moved this to
modelsdir
__init__.py
model1.py
class Model1
model2.py
class Model2
models.py
from modelsdir.model1 import Model1
from modelsdir.model2 import Model2
After this change, makemigraitons and makemigrations myapp no longer detect any changes done on the models. Any idea how to fix this?
EDIT:
I have since moved to this: removed models.py and renamed modelsdir to models
now it looks like so:
models
__init__.py
from .model1 import Model1
from .model2 import Model2
model1.py
model2.py
Unfortunately this still doesn't detect any changes.
I have a django project that is structured this way, and I had to add this in my modelsdir/__init__.py file:
from .model1 import *
from .model2 import *
I also didn't keep the original models.py file in my top-level app folder.
This is on Django 1.10/1.11 and Python 3.
I understand that if I want to establish a foreign key relationship for this model:
class MyModel(models.Model):
pass
... from this location
/myproject
/myapp
models.py (MyModel)
... I can use a "string-named" relationship to avoid import-time errors.
# this is in a different app within myproject
class DependentModel(models.Model):
my_model = models.ForeignKey('mayapp.MyModel')
But how do I establish that same ForeignKey relationship with a string when my models have been distributed across a models dir (e.g. not contained within one models.py file)
/myproject
/myapp
/models
/one.py (MyModel)
/two.py (other models)
(Assume that I can't just import the model directly, ala from myapp.models.one import MyModel)
Aha! I should have taken just a couple more minutes to search; this is possible simply by importing your models from within the __init__.py file inside the models dir:
# myproject/myapp/models/__init__.py
from one import MyModel
Related question: How do I separate my models out in django?
I'm trying to port my project to use Django 1.7. Everything is fine except 1 thing. Models inside tests folders.
Django 1.7 new migrations run migrate command internally. Before syncdb was ran. That means if a model is not included in migrations - it won't be populated to DB (and also to test DB). That's exactly what I'm experiencing right now.
What I do is:
In my /app/tests/models.py I have dummy model: class TestBaseImage(BaseImage): pass
All it does is to inherit from an abstract BaseImage model.
Then in tests I create instances of that dummy model to test it.
The problem is that it doesn't work any more. It's not included in migrations (that's obvious as I don't want to keep my test models in a production DB). Running my tests causes DB error saying that table does not exist. That makes sense as it's not included in migrations.
Is there any way to make it work with new migrations system? I can't find a way to "fix" that.
Code I use:
app/tests/models.py
from ..models import BaseImage
class TestBaseImage(BaseImage):
"""Dummy model just to test BaseImage abstract class"""
pass
app/models.py
class BaseImage(models.Model):
# ... fields ...
class Meta:
abstract = True
factories:
class BaseImageFactory(factory.django.DjangoModelFactory):
"""Factory class for Vessel model"""
FACTORY_FOR = BaseImage
ABSTRACT_FACTORY = True
class PortImageFactory(BaseImageFactory):
FACTORY_FOR = PortImage
example test:
def get_model_field(model, field_name):
"""Returns field instance"""
return model._meta.get_field_by_name(field_name)[0]
def test_owner_field(self):
"""Tests owner field"""
field = get_model_field(BaseImage, "owner")
self.assertIsInstance(field, models.ForeignKey)
self.assertEqual(field.rel.to, get_user_model())
There is a ticket requesting a way to do test-only models here
As a workaround, you can decouple your tests.py and make it an app.
tests
|--migrations
|--__init__.py
|--models.py
|--tests.py
You will end up with something like this:
myapp
|-migrations
|-tests
|--migrations
|--__init__.py
|--models.py
|--tests.py
|-__init__.py
|-models.py
|-views.py
Then you should add it to your INSTALLED_APPS
INSTALLED_APPS = (
# ...
'myapp',
'myapp.tests',
)
You probably don't want to install myapp.tests in production, so you can keep separate settings files. Something like this:
INSTALLED_APPS = (
# ...
'myapp',
)
try:
from local_settings import *
except ImportError:
pass
Or better yet, create a test runner and install your tests there.
Last but not least, remember to run python manage.py makemigrations
Here's a workaround that seems to work. Trick the migration framework into thinking that there are no migrations for your app. In settings.py:
if 'test' in sys.argv:
# Only during unittests...
# myapp uses a test-only model, which won't be loaded if we only load
# our real migration files, so point to a nonexistent one, which will make
# the test runner fall back to 'syncdb' behavior.
MIGRATION_MODULES = {
'myapp': 'myapp.migrations_not_used_in_tests'
}
I found the idea on the first post in ths Django dev mailing list thread, and it's also currently being used in Django itself, but it may not work in future versions of Django where migrations are required and the "syncdb fallback" is removed.