Как написать однофайловое приложение Django?

Я хочу написать очень маленькое приложение Django в одном файле, требующее все соответствующие модули и прочее, а затем иметь возможность запускать это как обычный Python script, например:

$ python myapp.py

Можно предположить, что я не буду отображать HTML, поэтому мне не нужны шаблоны (я вернусь JSON или некоторые другие автоматически сгенерированная строка).

Ответ 1

Возможно, вы захотите рассмотреть библиотеку Саймона Уиллисона:

Из readme:

djng - это микро-структура, которая зависит от макро-рамки (Django).

Мое определение микро-рамки: что-то, что позволяет вам создать целую Веб-приложение Python в одном модуле:

import djng

def index(request):
    return djng.Response('Hello, world')

if __name__ == '__main__':
    djng.serve(index, '0.0.0.0', 8888)

[...]

Ответ 2

Начало работы с Django также может быть довольно простым. Здесь 10-строчный однофайльный Django webapp:

import os
from django.conf.urls.defaults import patterns
from django.http import HttpResponse
filepath, extension = os.path.splitext(__file__)
ROOT_URLCONF = os.path.basename(filepath)

def yoohoo(request):
    return HttpResponse('Yoohoo!')

urlpatterns = patterns('', (r'^hello/$', yoohoo))

Подробнее см. мой блог Минимальный Django.

Ответ 3

Это простая CMS, реализованная в Django, как один файл. Он был написан Paul Bissex. Некоторые из них были "сыграны в гольф" и могут сделать с небольшим расширением, но все еще относительно легко читать.

Источник исчез из своего пастабина, но я сохранил его:

#!/usr/bin/env python
"""
jngo -- The unhealthily compressed Django application.

Usage: ./jngo.py

Assuming a working install of Django (http://djangoproject.com/) and SQLite
(http://sqlite.org), this script can be executed directly without any other 
preparations -- you don't have to do `setup.py install`, it doesn't 
need to be on your Python path, you don't need to set DJANGO_SETTINGS_MODULE,
you don't need a webserver. You don't even need content -- the first time it 
run, it will create a SQLite database in the same directory as the script 
and populate it with sample pages.

Features:

* Editable content on all pages
* Dynamically generated navigation buttons
* Optional private-access pages
* Optional per-page comments
* RSS feed of latest comments, with autodiscovery

Author: Paul Bissex <[email protected]>
URL: http://news.e-scribe.com/
License: MIT

FAQS: 

Q: Should I use this as an example of excellent Django coding practices?
A: Um, no. This is pretty much the opposite of excellent Django coding practices.

Q: Why did you do such a terrible thing?
A: At first, it was just a perverse experiment. It ended up being a
good way to refresh my memory on some Django internals, by trying all
kinds of things that broke in weird ways.
"""

#--- Settings ---
NAME = ROOT_URLCONF = "jngo"
DEBUG = TEMPLATE_DEBUG = True
SITE_ID = 3000
HOSTNAME_AND_PORT = "127.0.0.1:8000"
DATABASE_ENGINE = "sqlite3"
DATABASE_NAME = NAME + ".db"
INSTALLED_APPS = ["django.contrib.%s" % app for app in "auth admin contenttypes sessions sites flatpages comments".split()]
TEMPLATE_LOADERS = ('django.template.loaders.app_directories.load_template_source', NAME + '.template_loader')
MIDDLEWARE_CLASSES = ('django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware')
TEMPLATE_CONTEXT_PROCESSORS = (NAME + '.context_processor', "django.core.context_processors.auth", "django.core.context_processors.request")

#--- Template loader and templates ---
def template_loader(t, _):
    from django.template import TemplateDoesNotExist
    try:
        return {
            'base.html': """<html><head><title>{{ flatpage.title }}</title><link rel='alternate' type='application/rss+xml' href='/feed/'><style type="text/css">body { margin: 15px 50px; background: #eee; color: #343; font-family: sans-serif; } ul { padding: 0; } li { display: inline; background: #383; padding: 4px 8px; margin: 3px; } li:hover { background: #252; } dd { border-bottom: 1px dotted #666; } a { color: #383; text-decoration: none; } li a { color: #fff; } .anav { background: #141; } .rnav a { color: #ff4; } .error { color: #e22; } #footer { border-top: 1px dotted #555; font-size: 80%; color: #555; margin-top: 15px } #comments { background: #ddd; margin-top: 20px; padding: 10px; } dt { font-weight: bold; margin-top: 1em; }</style></head><body><ul>{% for nav in navs %}<li class="{% ifequal nav.url flatpage.url %}anav {% endifequal %}{% if nav.registration_required %}rnav {% endif %}"><a href="{{ nav.url }}">{{ nav.title }}</a></li>{% endfor %}</ul>{% block content %}{% endblock %}<div id="footer">{% if request.user.is_staff %}<a href="javascript:(function(){if(typeof%20ActiveXObject!='undefined'){var%20x=new%20ActiveXObject('Microsoft.XMLHTTP')}else%20if(typeof%20XMLHttpRequest!='undefined'){var%20x=new%20XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var%20type=x.getResponseHeader('x-object-type');var%20id=x.getResponseHeader('x-object-id');}catch(e){return;}document.location='/admin/'+type.split('.').join('/')+'/'+id+'/';})()">Edit this page</a> (as staff user <a href="/admin/">{{ request.user }}</a>)<br>{% endif %}Powered by <a href="http://djangoproject.com/">Django</a> {{ version }}<br></div></body></html>""",
            'flatpages/default.html': """{% extends "base.html" %}{% load comments %}{% block content %}<h1>{{ flatpage.title }}</h1>{{ flatpage.content }}{% if flatpage.enable_comments %}<div id="comments">{% get_free_comment_list for flatpages.flatpage flatpage.id as comments %}<h3>Comments!</h3><dl>{% for comment in comments %}{% include "comment.html" %}{% endfor %}</dl>{% free_comment_form for flatpages.flatpage flatpage.id %}</div>{% endif %}{% endblock %}""",
            'comments/free_preview.html': """{% extends "base.html" %}{% block content %}<h1>Comment preview</h1><dl>{% include "comment.html" %}</dl><form action='.' method='post'><input type='hidden' name='gonzo' value='{{ hash }}'><input type='hidden' name='options' value='{{ options }}'><input type='hidden' name='comment' value='{{ comment.comment }}'><input type='hidden' name='person_name' value='{{ comment.person_name }}'><input type='hidden' name='target' value='{{ target }}'><input type='submit' name='post' value='Post comment'></form>{% endblock %}""", 
            'comments/posted.html': """{% extends "base.html" %}{% block content %}<h1>Comment posted</h1><p>Thanks for posting!</p> <p><a href='{{ object.get_absolute_url }}'>Continue</a></p>{% endblock %}""",
            'comment.html': """<dt>{{ comment.person_name }} said:</dt> <dd>{{ comment.comment }}</dd>""",
            'registration/login.html': """{% extends "base.html" %}{% block content %}{% if form.has_errors %}<h2 class="error">Wrong!</h2>{% endif %}<p>This page is top secret, so you need to log in.</p><form method="post" action=".">Username: {{ form.username }}<br>Password: {{ form.password }}<br><input type="submit" value="login"><input type="hidden" name="next" value="{{ next }}"></form>{% endblock %}"""
            }[t], ''
    except KeyError:
        raise TemplateDoesNotExist
template_loader.is_usable = True

#--- Context processor ---
def context_processor(request):
    from django.contrib.flatpages.models import FlatPage
    navs = FlatPage.objects.all().values("url", "title", "registration_required")
    from django import get_version
    return { 'navs': navs, 'version': get_version() }

#--- RSS Feed (hacky wrapper function needed because of jngo one-file setup) ---
def feed(*args, **kwargs):
    from django.contrib.comments.feeds import LatestFreeCommentsFeed
    return LatestFreeCommentsFeed(*args, **kwargs)

#--- URLconf ---
from django.conf.urls.defaults import *
urlpatterns = patterns("", 
    (r"^admin/", include("django.contrib.admin.urls")), 
    (r"^comments/", include("django.contrib.comments.urls.comments")), 
    (r"^accounts/login/$", "django.contrib.auth.views.login"),
    (r"^(feed)/$", "django.contrib.syndication.views.feed", {'feed_dict': {'feed': feed}}),
    )

#--- Execution ---
if __name__ == "__main__":
    import os, sys
    from django.core.management import call_command
    here = os.path.dirname(__file__)
    sys.path.append(here)
    os.environ["DJANGO_SETTINGS_MODULE"] = NAME
    if not os.path.isfile(os.path.join(here, DATABASE_NAME)):
        from django.contrib.auth.create_superuser import createsuperuser
        from django.contrib.flatpages.models import FlatPage
        from django.contrib.sites.models import Site
        call_command("syncdb")
        createsuperuser()
        site_obj = Site.objects.create(id=SITE_ID, domain=HOSTNAME_AND_PORT)
        FlatPage.objects.create(url="/", title="Home", content="Welcome to %s!" % NAME).sites.add(site_obj)
        FlatPage.objects.create(url="/stuff/", enable_comments=True, title="Stuff", content="This is a page about stuff.").sites.add(site_obj)
        FlatPage.objects.create(url="/topsecret/", title="Top Secret", content="Now you know.", registration_required=True).sites.add(site_obj)
    call_command("runserver", HOSTNAME_AND_PORT)

Ответ 4

Протестировано с помощью Django 1.7

#!/usr/bin/env python
import os
import sys
from django.conf import settings
from django.conf.urls import patterns, include, url
from django.http import HttpResponse

filename = os.path.splitext(os.path.basename(__file__))[0]

urlpatterns = patterns('',
    url(r'^$', '%s.home' % filename, name='home'),
)

def home(request):
    return HttpResponse('hello')

if __name__ == "__main__":
    settings.configure(
        DEBUG=True,
        MIDDLEWARE_CLASSES = [],
        ROOT_URLCONF = filename
    )

    from django.core.management import execute_from_command_line
    execute_from_command_line([sys.argv[0], 'runserver'])

Ответ 5

Тогда вам не нужен Django. Вам нужно именно то, что micropy.

Ответ 6

Большинство примеров файлов django для одного файла, найденных в Интернете, не поддерживают модель, поскольку django каким-то образом требует, чтобы модели были объявлены в файле models.py в каждом INSTALLED_APP. Наконец, я нашел пример, который включает поддержку модели: -

http://fahhem.com/blog/2011/10/django-models-without-apps-or-everything-django-truly-in-a-single-file/

Ссылка на django wiki на создание моделей также стоит прочитать. И полный код, который также включает администратора: -

https://gist.github.com/2219751

Ответ 7

Ну, самый простой способ сделать это - имитировать проект arbo django в одном файле. Поэтому в одном модуле убедитесь, что есть:

Root_module :
    Root_module.settings
    Root_module.urls
    Root_module.app_in_the_module
    Root_module.app_in_the_module.models
    Root_module.app_in_the_module.views

Затем код является нормальным проектом Django. То, что вы должны знать, это то, что Django не нуждается в каком-либо конкретном месте. Стандартные имена и пути находятся в состоянии взлома, соглашения, в худшем случае, ярлыка, чтобы вы не определяли настройку.

Если вы знаете Django очень хорошо, вам даже не нужно имитировать арбо, просто напишите вам приложение django, сделав все данные из вышеуказанных модулей взаимосвязанными, как они должны быть.

Ответ 8

John answer, цитируя приложение jngo от Paul Bissex, впечатляет, но он грустно разбился под Django 1.7.

Я сделал кучу копания и создал μDjango, чтобы показать, как запустить класс модели с базой данных SQLite в одном файле Python. Я планирую использовать его для публикации вопросов о классах Django. Если вы хотите, чтобы некоторые из других частей были из jngo, вы могли бы перенести их на это. Fahrzin Hemmati опубликовал пример, который кажется похожим и включает полный Django: веб-сервер, модели, команды управления. Я еще не пробовал.

# Tested with Django 1.9.2
import sys

import django
from django.apps import apps
from django.apps.config import AppConfig
from django.conf import settings
from django.db import connections, models, DEFAULT_DB_ALIAS
from django.db.models.base import ModelBase

NAME = 'udjango'


def main():
    setup()

    class Person(models.Model):
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=30)

    syncdb(Person)

    p1 = Person(first_name='Jimmy', last_name='Jones')
    p1.save()
    p2 = Person(first_name='Bob', last_name='Brown')
    p2.save()

    print ', '.join([p.first_name for p in Person.objects.all()])


def setup():
    DB_FILE = NAME + '.db'
    with open(DB_FILE, 'w'):
        pass  # wipe the database
    settings.configure(
        DEBUG=True,
        DATABASES={
            DEFAULT_DB_ALIAS: {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': DB_FILE}},
        LOGGING={'version': 1,
                 'disable_existing_loggers': False,
                 'formatters': {
                    'debug': {
                        'format': '%(asctime)s[%(levelname)s]'
                                  '%(name)s.%(funcName)s(): %(message)s',
                        'datefmt': '%Y-%m-%d %H:%M:%S'}},
                 'handlers': {
                    'console': {
                        'level': 'DEBUG',
                        'class': 'logging.StreamHandler',
                        'formatter': 'debug'}},
                 'root': {
                    'handlers': ['console'],
                    'level': 'WARN'},
                 'loggers': {
                    "django.db": {"level": "WARN"}}})
    app_config = AppConfig(NAME, sys.modules['__main__'])
    apps.populate([app_config])
    django.setup()
    original_new_func = ModelBase.__new__

    @staticmethod
    def patched_new(cls, name, bases, attrs):
        if 'Meta' not in attrs:
            class Meta:
                app_label = NAME
            attrs['Meta'] = Meta
        return original_new_func(cls, name, bases, attrs)
    ModelBase.__new__ = patched_new


def syncdb(model):
    """ Standard syncdb expects models to be in reliable locations.

    Based on https://github.com/django/django/blob/1.9.3
    /django/core/management/commands/migrate.py#L285
    """
    connection = connections[DEFAULT_DB_ALIAS]
    with connection.schema_editor() as editor:
        editor.create_model(model)

main()

Ответ 9

Вы также можете взглянуть на web.py. (Tutorial)

Это еще одна компактная, но мощная веб-фреймворк.

Пример с главной страницы:

import web

urls = ('/(.*)', 'hello')
app = web.application(urls, globals())

class hello:        
    def GET(self, name):
        if not name: 
            name = 'world'
        return 'Hello, ' + name + '!'

if __name__ == "__main__":
    app.run()

Ответ 10

Приведенный ниже код представляет один файл приложения django. Он был бесстыдно скопирован из этой интересной главы книги и может быть запущен с помощью python singlefile.py runserver (если файл называется singlefile.py). Работает как минимум в Django 1.9 и 2.0.

import sys
from django.conf import settings

settings.configure(
    DEBUG=True,
    SECRET_KEY='thisisthesecretkey',
    ROOT_URLCONF=__name__,
    MIDDLEWARE_CLASSES=('django.middleware.common.CommonMiddleware',
                        'django.middleware.csrf.CsrfViewMiddleware',
                        'django.middleware.clickjacking.XFrameOptionsMiddleware')
)

from django.conf.urls import url
from django.http import HttpResponse


def index(request): return HttpResponse('Hello World')

urlpatterns = (url(r'^$', index), )

if __name__ == "__main__":
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

... это очень похоже на ответ un1t, за исключением некоторых деталей.

Ответ 11

Вот рабочий пример для Django 2.2

import os
import sys
from django.conf import settings
from django.core.management import execute_from_command_line
from django.http import HttpResponse
from django.urls import path

fname = os.path.splitext(os.path.basename(__file__))[0]
urlpatterns = [path(r'', lambda r: HttpResponse('Hello, world!'))]

if __name__ == "__main__":
    settings.configure(DEBUG=True, MIDDLEWARE_CLASSES=[], ROOT_URLCONF=fname)
    execute_from_command_line([sys.argv[0], 'runserver'])