5

I have a Django project and I wanted to generate some objects (from the models)

What I'm trying to get at : Standalone Python Script to create bunch of objects and/or filter,delete.

after importing the model with from apps.base.models import MyModel and setting up the configuration as the previous StackOverflow Questions suggested I was not able to run the script.

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myProject.settings")
import django

django.setup()
from apps.base.models import MyModel

Please note that this is on Django version 2.0.6 [Django 2.0+].

Correct settings have been used, (i.e. myProject.settings)

  • After properly configuring everything else I get the following error:

    • RuntimeError: Model class apps.base.models.MyModel doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

Settings:

The full GitHub link is here: https://github.com/teratzu/Razred-Management

innicoder
  • 2,612
  • 3
  • 14
  • 29
  • 1
    make sure you have this script in this path project -> scripts -> script.py – Ojas Kale Jun 28 '18 at 18:07
  • `ModuleNotFoundError: No module named 'MyProject'`// This would be after moving it to a /script/script.py – innicoder Jun 28 '18 at 18:29
  • ohh in here you dont need that line at all. just import models and you should be good to go – Ojas Kale Jun 28 '18 at 19:06
  • Why are you importing a base model class? You need to import the actual models. – Daniel Roseman Jun 28 '18 at 19:32
  • That's just where my models are stored. MyProject>apps>base>models .My app is called `base`. Ojas, that's not the line that's the error that I get when following your instructions. Sorry that didn't work. – innicoder Jun 28 '18 at 21:32
  • 1
    Could you add your project's `settings.py`, particularly `INSTALLED_APPS`, and the directory layout of your project? – Cole Jul 02 '18 at 03:27
  • 1
    Did you add your app name to `INSTALLED_APPS`? Can you share `myproject.settings` module? – hoefling Jul 02 '18 at 09:23
  • 1
    Also, the import looks suspicious, I also suggest you show the project directory structure. – hoefling Jul 02 '18 at 09:26
  • Added github project with elvirscript.py that should be the executable. – innicoder Jul 02 '18 at 22:16
  • I know you asked for a standalone script, but this sort of thing is more Django-y to implement as a management command: https://docs.djangoproject.com/en/2.0/howto/custom-management-commands/ – AKX Jul 03 '18 at 08:09
  • @AKX I'm thankful, however, I do need the standalone version. I'll review the answers and hopefully, everything should be all good. There's already a generator for this type of action I could use that but I need to be able to customize and interact with my project in a way that suits me. Hereby I thank everyone for their contributions including questions and comments. – innicoder Jul 03 '18 at 22:44

3 Answers3

2

I git clone'd your Django project's repository and got it working doing the following:


1. Move your Project's apps to the root of the project

This is a common Django standard. What I did was ./manage.py startapp base to start fresh and then add all of the files/directories from the apps/base directory to the base directory that was just made. It should look like this in the root directory of your project (where DzenanElvir & base are directories):

(.env) Cole:StackOverflow/Razred-Management ‹master*›$ ls -l              
total 512
drwxr-xr-x   9 Cole  staff     288 Jul  2 23:50 DzenanElvir
-rw-r--r--   1 Cole  staff    1055 Jul  2 23:48 LICENSE
-rw-r--r--   1 Cole  staff     299 Jul  2 23:48 Pipfile
-rw-r--r--   1 Cole  staff   27343 Jul  2 23:48 Pipfile.lock
-rw-r--r--   1 Cole  staff      72 Jul  2 23:48 Procfile
-rw-r--r--   1 Cole  staff    1160 Jul  2 23:48 README.md
drwxr-xr-x  15 Cole  staff     480 Jul  3 00:06 base
...

And like this in your new base app directory:

(.env) Cole:Razred-Management/base ‹master*›$ ls -l
total 80
-rw-r--r--   1 Cole  staff    53 Jul  2 23:58 __init__.py
drwxr-xr-x  11 Cole  staff   352 Jul  3 00:08 __pycache__
-rw-r--r--   1 Cole  staff    63 Jul  3 00:04 admin.py
-rw-r--r--   1 Cole  staff    83 Jul  3 00:04 apps.py
-rw-r--r--   1 Cole  staff  2004 Jul  2 23:58 forms.py
drwxr-xr-x   5 Cole  staff   160 Jul  3 00:06 migrations
-rw-r--r--   1 Cole  staff  1126 Jul  2 23:58 models.py
-rw-r--r--   1 Cole  staff  2236 Jul  2 23:58 postsView.py
-rw-r--r--   1 Cole  staff  1913 Jul  2 23:58 pregledView.py
drwxr-xr-x   3 Cole  staff    96 Jul  2 23:58 templates
-rw-r--r--   1 Cole  staff    60 Jul  3 00:04 tests.py
-rw-r--r--   1 Cole  staff  1425 Jul  2 23:58 urls.py
-rw-r--r--   1 Cole  staff   343 Jul  2 23:58 views.py

2. Add base app to INSTALLED_APPS the more explicit way

Notice the new base/apps.py file, we're going to use it's AppConfig-based class to declare it an installed app. Replace INSTALLED_APPS's 'base' declaration with 'base.apps.BaseConfig'.


3. Rework your settings.BASE_DIR variable

Typically, the file path to a Django project's settings file is projectname/settings.py but for separated settings it's projectname/settings/<env_name>.py.

Therefore, BASE_DIR needs to be reworked from:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

to:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

to account for the extra directory between your project root and your settings file.


4. Migrate models

Perform ./manage.py makemigrations && ./manage.py migrate.

Also, as a side-note, migrations directories shouldn't be in .gitignore. Read: Should I be adding the Django migration files in the .gitignore file?



And with that I was able to run the below script, at the root of the Django project, without error:

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DzenanElvir.settings.base")
import django

django.setup()
from base.models import ModelRazred
Cole
  • 1,715
  • 13
  • 23
  • Sorry, I don't want the project structure reworked. What's the solution to get it working on the already existing project? – innicoder Jul 05 '18 at 02:58
  • @ElvirMuslic I've added another solution without involving a project structure rework, although I advise against creating non-reusable apps. – Cole Jul 05 '18 at 03:47
2

I see two issues in the current project structure.

Missing AppConfig

As the other answer suggested correctly, you need to provide an AppConfig subclass to register your application; see Configuring applications section in Django docs.

However, having an apps package for namespace declarations is totally fine, especially if you plan to have multiple apps; in fact, this is what Django does itself, bundling all batteries apps under the django.contrib namespace. At my work, we also use common namespaces to organize Django apps: mycompany.internal.someapp, mycompany.customers.otherapp etc.

So you don't need to move the files. Just reference the apps.base correctly: create a file apps/base/apps.py with the content:

# apps/base/apps.py
from django.apps import AppConfig


class MyBaseAppConfig(AppConfig):
    name = 'apps.base'

Now reference the created config class of the app apps.base in the settings:

# DzenanElvir/settings/base.py

INSTALLED_APPS = [
    ...
    # Local apps
    'apps.base.apps.MyBaseAppConfig',
]

If apps should be a package, treat it like one

So you declared the apps package (by placing an__init__.py file in the apps dir). However, you never use the package, instead fiddling the sys.path by adding the apps dir to it. If the app should be importable under apps.base, remove the path fiddling line in the DzenanElvir/settings.base.py:

# remove this line:
path.append(os.path.join(PROJECT_ROOT, "apps"))

and use the right imports throughout the app, replacing:

  • from .models with from apps.base.models
  • from .forms with from apps.base.forms
  • from .pregledView with from apps.base.pregledView
  • from .postsView with from apps.base.postsView

Also, don't forget to replace

urlpatterns = [
    url(r'', include('base.urls')),
    ...
]

with

urlpatterns = [
    url(r'', include('apps.base.urls')),
    ...
]

in DzenanElvir/urls.py and you should be good to go.

hoefling
  • 59,418
  • 12
  • 147
  • 194
  • The Django package isn’t a Django project itself, `contrib` is simply a directory of reusable Django apps to solve common Web-development problems (as it says in the docs, https://docs.djangoproject.com/en/2.1/ref/contrib/). – Cole Jul 03 '18 at 16:21
  • _The Django package isn’t a Django project itself_ - I never said that. _contrib is simply a directory of reusable Django apps_ - in fact, `contrib` is a python package that is an example of how to bundle multiple Django apps under a single package. You do realize that `startproject` and `startapp` are just handy shortcuts for creating an initial project structure, nothing more? And that you can develop Django apps without creating a Django project, having an app that _is_ the project or just have multiple apps in the same codebase? – hoefling Jul 03 '18 at 16:55
  • I was under the impression that the justification from, "... having an apps package for namespace declarations is totally fine, especially if you plan to have multiple apps; in fact, this is what Django does itself ..." inferred that the Django Python package is a Django project because it puts its reusable (and not installed) apps in a directory. I was simply pointing out that Python package != Django project's apps, so the Django package doesn't *actually* do that. Maybe I misinterpreted what you said – Cole Jul 03 '18 at 19:00
1

I've once again git clone'd your Django project's repository and got it working, without having to rework your project's structure, by doing the following:


1. Rework your settings.BASE_DIR variable

Typically, the file path to a Django project's settings file is projectname/settings.py but for separated settings it's projectname/settings/<env_name>.py.

Therefore, BASE_DIR needs to be reworked from:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

to:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

to account for the extra directory between your project root and your settings file.


2. Change app declaration in INSTALLED_APPS:

Your app's dotted location from BASE_DIR is apps.base. Replace the current declaration, base, with apps.base.


3. Fix dotted paths:

If any errors arise from dotted paths, check the traceback as it will point to the issue. For example, your current urls.py will likely cause an issue because there is a dotted path to base.urls whereas it should be the explicit apps.base.urls. For instance:

urlpatterns = [
    url(r'', include('base.urls')),
    ...
]

To the following:

urlpatterns = [
    url(r'', include('apps.base.urls')),
    ...
]

4. Migrate models

Perform ./manage.py makemigrations && ./manage.py migrate.

Also, as a side-note, migrations directories shouldn't be in .gitignore. Read: Should I be adding the Django migration files in the .gitignore file?



And with that I was able to run the below script, at the root of the Django project, without error:

import os
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DzenanElvir.settings.base")
django.setup()
from apps.base.models import ModelRazred
innicoder
  • 2,612
  • 3
  • 14
  • 29
Cole
  • 1,715
  • 13
  • 23
  • When I try to change the BASE_DIR I get : `ModuleNotFoundError: No module named 'base'` and if I change the `base` to `apps.base` I get : `RuntimeError: Model class base.models.ModelRazred doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.` – innicoder Jul 05 '18 at 10:40
  • It might be caused by other things using dotted path. Check the traceback. Other than that, I don't know what to tell you – the standalone script works on my end – Cole Jul 05 '18 at 20:26
  • No, actually the changes that you've told me to make caused additional errors that I didn't have before, such as the `BASE_DIR` change and it's not possible that you've ran the changes and that it worked as I did try reworking everything from sratch. – innicoder Jul 07 '18 at 16:55
  • @ElvirMuslic Why would someone lie about that lol. And if you don't understand the reason `BASE_DIR` had to be changed then I think you need to print it to the console both ways and see where it points, you will understand then. – Cole Jul 07 '18 at 16:58
  • BASE_DIR was previously pointing to The whole Project I.E. `Razred-Management` now it's pointing to 'DzenanElvir'. It's not about whether you lie or not but IF it works or not. It doesn't. I just said that I followed each step and gotten multiple other errors, which I've been unable to trace the issue to making it even harder than it was before. Perhaps you didn't notice but I've specifically stated that this is for Django > 2.0 as the previous solutions were not compatible. – innicoder Jul 07 '18 at 17:05
  • '`Please note that this is on Django version 2.0.6 [Django 2.0+].`' - main post. You could create a virtual env and fullsetup.py and get all the same settings, I've also ran this on Windows 10. – innicoder Jul 07 '18 at 17:07
  • @ElvirMuslic How could wrapping your original `BASE_DIR` in another `os.path.dirname` go forward a directory when `os.path.dirname` returns the directory of whatever valid directory/file you enter into it? It previously pointed to `DzenanElvir`, with the correction it points to `Razred-Management` (the project base). – Cole Jul 07 '18 at 17:10
  • I have no idea it worked before. I don't remember why it works but it does. Once I change the BASE_DIR and apps > `apps.base` I get : `No module named 'base'` – innicoder Jul 07 '18 at 17:16
  • The issue was : Also, don't forget to replace urlpatterns = [ url(r'', include('base.urls')), ... ] with urlpatterns = [ url(r'', include('apps.base.urls')), ... ] in DzenanElvir/urls.py and you should be good to go. If the other had only added the same response as yours with this I would've given the bounty to him, add this to your answer. – innicoder Jul 07 '18 at 17:18
  • @ElvirMuslic That's why I said "It might be caused by other things using dotted path. Check the traceback.". I will add it to my answer for clarification. – Cole Jul 07 '18 at 17:23
  • I did check but wasn't able that was too vague of a comment. You were supposed to include the working answer. – innicoder Jul 07 '18 at 17:24
  • @ElvirMuslic I thought the traceback was obvious, my mistake. Will be more explicit in the future. I've added the extra step. – Cole Jul 07 '18 at 17:30