如何编写单文件Django应用程序?

时间:2009-08-19 04:58:30

标签: python django web-applications

我想在一个文件中编写一个非常小的Django应用程序,需要所有相应的模块和东西,然后能够将其作为普通的Python脚本运行,如下所示:

$ python myapp.py

您可以假设我不会呈现HTML,因此我不需要模板(我将返回JSON或其他一些自动生成的字符串)。

12 个答案:

答案 0 :(得分:13)

您可能想要考虑Simon Willison的图书馆:

来自readme

  

djng是一个依赖于宏框架(Django)的微框架。

     

我对微框架的定义:可以让你创建一个整体   单个模块中的Python Web应用程序:

import djng

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

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

[...]

答案 1 :(得分:12)

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))

查看我的博文Minimal Django了解详情。

答案 2 :(得分:8)

这是一个在Django中实现的简单CMS,作为单个文件。它由Paul Bissex撰写。其中一些已经“高尔夫”,可以做一些扩展,但它仍然相对容易阅读。

源码已从他的pastebin中消失,但我保存了它:

#!/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's 
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 <pb@e-scribe.com>
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's 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)

答案 3 :(得分:5)

使用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'])

答案 4 :(得分:2)

然后你需要的不是Django。 你需要的正是micropy所做的。

答案 5 :(得分:2)

网上发现的大多数单个文件django示例都缺乏对模型的支持,因为django不知何故需要在每个INSTALLED_APP的models.py文件中声明模型。最后,我找到了一个包含模型支持的示例: -

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

关于模型创建的django wiki的链接也值得一读。而完整的代码也包括admin: -

https://gist.github.com/2219751

答案 6 :(得分:1)

嗯,最简单的方法是在一个文件中模仿django项目arbo。所以在一个模块中,确保有:

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,你甚至不需要模仿arbo,只需写下你的django应用程序,让上面模块中的所有数据按照应有的方式互连。

答案 7 :(得分:1)

John's answer引用Paul Bissex的jngo app令人印象深刻,但在Django 1.7下它被遗忘了。

我做了一堆挖掘和created Django来展示如何在单个Python文件中运行带有SQLite数据库的模型类。我打算用它来发布关于Django模型类的问题。如果你想要一些来自jngo的其他部分,你可以将它们移植到它上面。 Fahrzin Hemmati发布的an example似乎相似,包括完整的Django:Web服务器,模型,管理命令。我还没有尝试过。

# 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()

答案 8 :(得分:1)

这是 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'])

答案 9 :(得分:1)

在网络上找到的大多数单文件 Django 示例都缺乏对模型的支持,因为 Django 以某种方式要求在每个 INSTALLED_APP 的 models.py 文件中声明模型,但这里有一个包含模型支持的示例:

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

关于模型创建的 link there to the Django wiki 也值得一读。

来自 k4ml 的完整代码,包括 admin(大约 2012 年): https://gist.github.com/2219751

为 Django 2.2 和 3.1 调整的 Hemmati 原始版本:

from os import path as osp
def rel_path(*p): return osp.normpath(osp.join(rel_path.path, *p))
rel_path.path = osp.abspath(osp.dirname(__file__))
this = osp.splitext(osp.basename(__file__))[0]
from django.conf import settings
SETTINGS = dict(
    DATABASES = {},
    DEBUG=True,
    TEMPLATE_DEBUG=True,
    ROOT_URLCONF = this
)
SETTINGS['DATABASES']={
    'default':{
        'ENGINE':'django.db.backends.sqlite3',
        'NAME':rel_path('db')
    }
}

if __name__=='__main__':
    settings.configure(**SETTINGS)

if __name__ == '__main__':
    from django.core import management
    management.execute_from_command_line()

from django.urls import path
from django.http import HttpResponse
def view_name(request):
    return HttpResponse('response text')
urlpatterns = [ path('',view_name) ]

from django.template.response import TemplateResponse
def view_name(request):
    return TemplateResponse(request, 'template.html', {'variable':'value'})

SETTINGS['TEMPLATE_DIRS'] = (rel_path(),),

from django.db import models
from django.apps import apps
class SomeModel(models.Model):
    class Meta: app_label = this
    __module__ = this
    field_name = models.CharField(max_length=10)

if __name__=='__main__':
    # override get_app to work with us
    def get_app(app_label,*a, **kw):
        if app_label==this:
            return sys.modules[__name__]
        return apps.get_app_config(app_label,*a,**kw).models_module
    models.get_app = get_app
    apps.app_configs[type(this+'.models',(),{'__file__':__file__})] = this

from django.core.management import get_commands, BaseCommand
class MyCommand(BaseCommand):
    def handle(self, **options):
        pass # do your stuff here like a normal command
if __name__ == '__main__':

    
    commands = get_commands()
    
    commands['management_command_name'] = MyCommand()
    

    

答案 10 :(得分:0)

您还可以查看web.py。 (Tutorial

这是另一个紧凑但功能强大的Web框架。

主页上的示例:

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()

答案 11 :(得分:0)

以下代码代表单个文件django应用。它是从this interesting book chapter中进行的无耻复制,可以使用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的答案,除了一些details