1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

newforms-admin: Merged to [6013]

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6014 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-08-25 23:31:05 +00:00
parent a6784e6821
commit 22e160945a
24 changed files with 304 additions and 89 deletions

View File

@ -95,6 +95,7 @@ answer newbie questions, and generally made Django that much better:
Alex Dedul
deric@monowerks.com
Max Derkachev <mderk@yandex.ru>
Sander Dijkhuis <sander.dijkhuis@gmail.com>
Jordan Dimov <s3x3y1@gmail.com>
dne@mayonnaise.net
Maximillian Dornseif <md@hudora.de>
@ -137,6 +138,7 @@ answer newbie questions, and generally made Django that much better:
Joe Heck <http://www.rhonabwy.com/wp/>
Joel Heenan <joelh-django@planetjoel.com>
hipertracker@gmail.com
Deryck Hodge <http://www.devurandom.org/>
Brett Hoerner <bretthoerner@bretthoerner.com>
Ian Holsman <http://feh.holsman.net/>
Kieran Holland <http://www.kieranholland.com>
@ -204,6 +206,7 @@ answer newbie questions, and generally made Django that much better:
Andreas Mock <andreas.mock@web.de>
Reza Mohammadi <reza@zeerak.ir>
Aljosa Mohorovic <aljosa.mohorovic@gmail.com>
Ramiro Morales <rm0@gmx.net>
Eric Moritz <http://eric.themoritzfamily.com/>
mrmachine <real.human@mrmachine.net>
Robin Munn <http://www.geekforgod.com/>
@ -213,6 +216,7 @@ answer newbie questions, and generally made Django that much better:
Fraser Nevett <mail@nevett.org>
Sam Newman <http://www.magpiebrain.com/>
Neal Norwitz <nnorwitz@google.com>
Todd O'Bryan <toddobryan@mac.com>
oggie rob <oz.robharvey@gmail.com>
Jay Parlar <parlar@gmail.com>
pavithran s <pavithran.s@gmail.com>
@ -228,10 +232,10 @@ answer newbie questions, and generally made Django that much better:
Daniel Poelzleithner <http://poelzi.org/>
polpak@yahoo.com
Matthias Pronk <django@masida.nl>
Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
Johann Queuniet <johann.queuniet@adh.naellia.eu>
J. Rademaker
Michael Radziej <mir@noris.de>
Ramiro Morales <rm0@gmx.net>
Massimiliano Ravelli <massimiliano.ravelli@gmail.com>
Brian Ray <http://brianray.chipy.org/>
remco@diji.biz

View File

@ -4,5 +4,6 @@ def get_version():
"Returns the version as a human-format string."
v = '.'.join([str(i) for i in VERSION[:-1]])
if VERSION[-1]:
v += '-' + VERSION[-1]
from django.utils.version import get_svn_revision
v = '%s-%s-%s' % (v, VERSION[-1], get_svn_revision())
return v

View File

@ -17,7 +17,7 @@ def login(request, template_name='registration/login.html'):
errors = manipulator.get_validation_errors(request.POST)
if not errors:
# Light security check -- make sure redirect_to isn't garbage.
if not redirect_to or '://' in redirect_to or ' ' in redirect_to:
if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
from django.conf import settings
redirect_to = settings.LOGIN_REDIRECT_URL
from django.contrib.auth import login

View File

@ -1,6 +1,9 @@
from django.utils.translation import ungettext, ugettext as _
from django.utils.encoding import force_unicode
from django import template
from django.template import defaultfilters
from django.conf import settings
from datetime import date, timedelta
import re
register = template.Library()
@ -67,3 +70,27 @@ def apnumber(value):
return value
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
register.filter(apnumber)
def naturalday(value, arg=None):
"""
For date values that are tomorrow, today or yesterday compared to
present day returns representing string. Otherwise, returns a string
formatted according to settings.DATE_FORMAT.
"""
try:
value = date(value.year, value.month, value.day)
except AttributeError:
# Passed value wasn't a date object
return value
except ValueError:
# Date arguments out of range
return value
delta = value - date.today()
if delta.days == 0:
return _(u'today')
elif delta.days == 1:
return _(u'tomorrow')
elif delta.days == -1:
return _(u'yesterday')
return defaultfilters.date(value, arg)
register.filter(naturalday)

View File

@ -24,7 +24,7 @@ class Command(NoArgsCommand):
except ImportError:
pass
sql_list = sql_flush(self.style)
sql_list = sql_flush(self.style, only_django=True)
if interactive:
confirm = raw_input("""You have requested a flush of the database.

View File

@ -30,7 +30,7 @@ class Command(BaseCommand):
raise CommandError("%r is not a valid port number." % port)
use_reloader = options.get('use_reloader', True)
admin_media_dir = options.get('admin_media_dir', '')
admin_media_path = options.get('admin_media_path', '')
shutdown_message = options.get('shutdown_message', '')
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
@ -42,7 +42,7 @@ class Command(BaseCommand):
print "Development server is running at http://%s:%s/" % (addr, port)
print "Quit the server with %s." % quit_command
try:
path = admin_media_dir or django.__path__[0] + '/contrib/admin/media'
path = admin_media_path or django.__path__[0] + '/contrib/admin/media'
handler = AdminMediaHandler(WSGIHandler(), path)
run(addr, int(port), handler)
except WSGIServerException, e:

View File

@ -7,4 +7,4 @@ class Command(NoArgsCommand):
def handle_noargs(self, **options):
from django.core.management.sql import sql_flush
return '\n'.join(sql_flush(self.style))
return '\n'.join(sql_flush(self.style, only_django=True))

View File

@ -13,6 +13,25 @@ def table_list():
cursor = connection.cursor()
return get_introspection_module().get_table_list(cursor)
def django_table_list(only_existing=False):
"""
Returns a list of all table names that have associated Django models and
are in INSTALLED_APPS.
If only_existing is True, the resulting list will only include the tables
that actually exist in the database.
"""
from django.db import models
tables = []
for app in models.get_apps():
for model in models.get_models(app):
tables.append(model._meta.db_table)
tables.extend([f.m2m_db_table() for f in model._meta.many_to_many])
if only_existing:
existing = table_list()
tables = [t for t in tables if t in existing]
return tables
def installed_models(table_list):
"Returns a set of all models that are installed, given a list of existing table names."
from django.db import connection, models
@ -181,10 +200,19 @@ def sql_reset(app, style):
"Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
return sql_delete(app, style) + sql_all(app, style)
def sql_flush(style):
"Returns a list of the SQL statements used to flush the database."
def sql_flush(style, only_django=False):
"""
Returns a list of the SQL statements used to flush the database.
If only_django is True, then only table names that have associated Django
models and are in INSTALLED_APPS will be included.
"""
from django.db import connection
statements = connection.ops.sql_flush(style, table_list(), sequence_list())
if only_django:
tables = django_table_list()
else:
tables = table_list()
statements = connection.ops.sql_flush(style, tables, sequence_list())
return statements
def sql_custom(app):

View File

@ -8,6 +8,7 @@ ImproperlyConfigured.
"""
from django.core.exceptions import ImproperlyConfigured
from django.db.backends import BaseDatabaseFeatures, BaseDatabaseOperations
def complain(*args, **kwargs):
raise ImproperlyConfigured, "You haven't set the DATABASE_ENGINE setting yet."
@ -21,13 +22,12 @@ class DatabaseError(Exception):
class IntegrityError(DatabaseError):
pass
class ComplainOnGetattr(object):
def __getattr__(self, *args, **kwargs):
complain()
class DatabaseOperations(BaseDatabaseOperations):
quote_name = complain
class DatabaseWrapper(object):
features = ComplainOnGetattr()
ops = ComplainOnGetattr()
features = BaseDatabaseFeatures()
ops = DatabaseOperations()
operators = {}
cursor = complain
_commit = complain

View File

@ -70,7 +70,7 @@ class DatabaseOperations(BaseDatabaseOperations):
return "DROP SEQUENCE %s;" % self.quote_name(get_sequence_name(table))
def field_cast_sql(self, db_type):
if db_type.endswith('LOB'):
if db_type and db_type.endswith('LOB'):
return "DBMS_LOB.SUBSTR(%s)"
else:
return "%s"

View File

@ -102,9 +102,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
cursor.execute("SET client_encoding to 'UNICODE'")
cursor = UnicodeCursorWrapper(cursor, 'utf-8')
if self.ops.postgres_version is None:
cursor.execute("SELECT version()")
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
return cursor
def typecast_string(s):

View File

@ -4,8 +4,17 @@ from django.db.backends import BaseDatabaseOperations
# used by both the 'postgresql' and 'postgresql_psycopg2' backends.
class DatabaseOperations(BaseDatabaseOperations):
def __init__(self, postgres_version=None):
self.postgres_version = postgres_version
def __init__(self):
self._postgres_version = None
def _get_postgres_version(self):
if self._postgres_version is None:
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT version()")
self._postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
return self._postgres_version
postgres_version = property(_get_postgres_version)
def date_extract_sql(self, lookup_type, field_name):
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
@ -52,28 +61,14 @@ class DatabaseOperations(BaseDatabaseOperations):
for sequence_info in sequences:
table_name = sequence_info['table']
column_name = sequence_info['column']
if column_name and len(column_name)>0:
# sequence name in this case will be <table>_<column>_seq
sql.append("%s %s %s %s %s %s;" % \
(style.SQL_KEYWORD('ALTER'),
style.SQL_KEYWORD('SEQUENCE'),
style.SQL_FIELD(self.quote_name('%s_%s_seq' % (table_name, column_name))),
style.SQL_KEYWORD('RESTART'),
style.SQL_KEYWORD('WITH'),
style.SQL_FIELD('1')
)
)
if column_name and len(column_name) > 0:
sequence_name = '%s_%s_seq' % (table_name, column_name)
else:
# sequence name in this case will be <table>_id_seq
sql.append("%s %s %s %s %s %s;" % \
(style.SQL_KEYWORD('ALTER'),
style.SQL_KEYWORD('SEQUENCE'),
style.SQL_FIELD(self.quote_name('%s_id_seq' % table_name)),
style.SQL_KEYWORD('RESTART'),
style.SQL_KEYWORD('WITH'),
style.SQL_FIELD('1')
)
)
sequence_name = '%s_id_seq' % table_name
sql.append("%s setval('%s', 1, false);" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(self.quote_name(sequence_name)))
)
return sql
else:
return []

View File

@ -64,7 +64,4 @@ class DatabaseWrapper(BaseDatabaseWrapper):
cursor.tzinfo_factory = None
if set_tz:
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
if self.ops.postgres_version is None:
cursor.execute("SELECT version()")
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
return cursor

39
django/utils/version.py Normal file
View File

@ -0,0 +1,39 @@
import django
import os.path
import re
def get_svn_revision(path=None):
"""
Returns the SVN revision in the form SVN-XXXX,
where XXXX is the revision number.
Returns SVN-unknown if anything goes wrong, such as an unexpected
format of internal SVN files.
If path is provided, it should be a directory whose SVN info you want to
inspect. If it's not provided, this will use the root django/ package
directory.
"""
rev = None
if path is None:
path = django.__path__[0]
entries_path = '%s/.svn/entries' % path
if os.path.exists(entries_path):
entries = open(entries_path, 'r').read()
# Versions >= 7 of the entries file are flat text. The first line is
# the version number. The next set of digits after 'dir' is the revision.
if re.match('(\d+)', entries):
rev_match = re.search('\d+\s+dir\s+(\d+)', entries)
if rev_match:
rev = rev_match.groups()[0]
# Older XML versions of the file specify revision as an attribute of
# the first entries node.
else:
from xml.dom import minidom
dom = minidom.parse(entries_path)
rev = dom.getElementsByTagName('entry')[0].getAttribute('revision')
if rev:
return u'SVN-%s' % rev
return u'SVN-unknown'

View File

@ -138,6 +138,29 @@ Examples:
You can pass in either an integer or a string representation of an integer.
naturalday
----------
**New in Django development version**
For dates that are the current day or within one day, return "today",
"tomorrow" or "yesterday", as appropriate. Otherwise, format the date using
the passed in format string.
**Argument:** Date formatting string as described in default tag now_.
.. _now: ../templates/#now
Examples (when 'today' is 17 Feb 2007):
* ``16 Feb 2007`` becomes ``yesterday``.
* ``17 Feb 2007`` becomes ``today``.
* ``18 Feb 2007`` becomes ``tomorrow``.
* Any other day is formatted according to given argument or the
`DATE_FORMAT`_ setting if no argument is given.
.. _DATE_FORMAT: ../settings/#date_format
flatpages
=========

View File

@ -207,14 +207,23 @@ the database until you explicitly call ``save()``.
The ``save()`` method has no return value.
Updating ``ForeignKey`` fields works exactly the same way; simply assign an
object of the right type to the field in question::
Saving ForeignKey and ManyToManyField fields
--------------------------------------------
joe = Author.objects.create(name="Joe")
entry.author = joe
Updating ``ForeignKey`` fields works exactly the same way as saving a normal
field; simply assign an object of the right type to the field in question::
cheese_blog = Blog.objects.get(name="Cheddar Talk")
entry.blog = cheese_blog
entry.save()
Django will complain if you try to assign an object of the wrong type.
Updating a ``ManyToManyField`` works a little differently; use the ``add()``
method on the field to add a record to the relation::
joe = Author.objects.create(name="Joe")
entry.authors.add(joe)
Django will complain if you try to assign or add an object of the wrong type.
How Django knows to UPDATE vs. INSERT
-------------------------------------

View File

@ -124,6 +124,13 @@ executed. This means that all data will be removed from the database, any
post-synchronization handlers will be re-executed, and the ``initial_data``
fixture will be re-installed.
The behavior of this command has changed in the Django development version.
Previously, this command cleared *every* table in the database, including any
table that Django didn't know about (i.e., tables that didn't have associated
models and/or weren't in ``INSTALLED_APPS``). Now, the command only clears
tables that are represented by Django models and are activated in
``INSTALLED_APPS``.
inspectdb
---------
@ -240,6 +247,7 @@ Executes the equivalent of ``sqlreset`` for the given appnames.
runfcgi [options]
-----------------
Starts a set of FastCGI processes suitable for use with any web server
which supports the FastCGI protocol. See the `FastCGI deployment
documentation`_ for details. Requires the Python FastCGI module from
@ -337,7 +345,7 @@ Refer to the description of ``sqlcustom`` for an explanation of how to
specify initial data.
sqlclear [appname appname ...]
--------------------------------------
------------------------------
Prints the DROP TABLE SQL statements for the given appnames.
@ -360,18 +368,23 @@ table modifications, or insert any SQL functions into the database.
Note that the order in which the SQL files are processed is undefined.
sqlflush
--------
Prints the SQL statements that would be executed for the `flush`_ command.
sqlindexes [appname appname ...]
----------------------------------------
--------------------------------
Prints the CREATE INDEX SQL statements for the given appnames.
sqlreset [appname appname ...]
--------------------------------------
------------------------------
Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given appnames.
sqlsequencereset [appname appname ...]
----------------------------------------------
--------------------------------------
Prints the SQL statements for resetting sequences for the given
appnames.

View File

@ -204,10 +204,6 @@ out a few points, we want to make sure they reflect the final state of things
at Django 1.0, not some intermediary step. In other words, we don't want to
spend a lot of energy creating screencasts yet, because Django APIs will shift.
In the meantime, though, check out this `unofficial Django screencast`_.
.. _unofficial Django screencast: http://www.throwingbeans.org/django_screencasts.html
Is Django a content-management-system (CMS)?
--------------------------------------------

View File

@ -344,7 +344,7 @@ development version. See the `Django 0.96 documentation`_ for the old behavior.
``ImageField``
~~~~~~~~~~~~~~
Like ``FileField``, but validates that the uploaded object is a valid
Like `FileField`_, but validates that the uploaded object is a valid
image. Has two extra optional arguments, ``height_field`` and
``width_field``, which, if set, will be auto-populated with the height and
width of the image each time a model instance is saved.

View File

@ -1557,7 +1557,7 @@ as a custom extension to the ``TextInput`` widget::
class CommentWidget(forms.TextInput):
def __init__(self, *args, **kwargs):
kwargs.setdefault('attrs',{}).update({'size': '40'})
super(forms.TextInput, self).__init__(*args, **kwargs)
super(CommentWidget, self).__init__(*args, **kwargs)
Then you can use this widget in your forms::
@ -1934,6 +1934,42 @@ will raise ``ValueError`` if the data doesn't validate.
``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback``
arguments that behave the same way as they do for ``form_for_model()``.
Let's modify the earlier `contact form`_ view example a little bit. Suppose we
have a ``Message`` model that holds each contact submission. Something like::
class Message(models.Model):
subject = models.CharField(max_length=100)
message = models.TextField()
sender = models.EmailField()
cc_myself = models.BooleanField()
You could use this model to create a form (using ``form_for_model()``). You
could also use existing ``Message`` instances to create a form for editing
messages. The earlier_ view can be changed slightly to accept the ``id`` value
of an existing ``Message`` and present it for editing::
def contact_edit(request, msg_id):
# Create the form from the message id.
message = get_object_or_404(Message, id=msg_id)
ContactForm = form_for_instance(message)
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/url/on_success/')
else:
form = ContactForm()
return render_to_response('contact.html', {'form': form})
Aside from how we create the ``ContactForm`` class here, the main point to
note is that the form display in the ``GET`` branch of the function
will use the values from the ``message`` instance as initial values for the
form field.
.. _contact form: `Simple view example`_
.. _earlier: `Simple view example`_
When should you use ``form_for_model()`` and ``form_for_instance()``?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1101,10 +1101,11 @@ To disable this behavior, just remove all entries from the ``ADMINS`` setting.
404 errors
----------
When ``DEBUG`` is ``False`` and your ``MIDDLEWARE_CLASSES`` setting includes
``CommonMiddleware``, Django will e-mail the users listed in the ``MANAGERS``
setting whenever your code raises a 404 and the request has a referer.
(It doesn't bother to e-mail for 404s that don't have a referer.)
When ``DEBUG`` is ``False``, ``SEND_BROKEN_LINK_EMAILS`` is ``True`` and your
``MIDDLEWARE_CLASSES`` setting includes ``CommonMiddleware``, Django will
e-mail the users listed in the ``MANAGERS`` setting whenever your code raises
a 404 and the request has a referer. (It doesn't bother to e-mail for 404s
that don't have a referer.)
You can tell Django to stop reporting particular 404s by tweaking the
``IGNORABLE_404_ENDS`` and ``IGNORABLE_404_STARTS`` settings. Both should be a

View File

@ -473,7 +473,7 @@ Once you have a ``Client`` instance, you can call any of the following methods:
data. For example::
>>> c = Client()
>>> c.get('/login/', {'name': 'fred', 'passwd': 'secret'})
>>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})
...will result in the evaluation of a POST request to this URL::

View File

@ -259,6 +259,22 @@ These concepts are represented by simple Python classes. Edit the
choice = models.CharField(max_length=200)
votes = models.IntegerField()
.. adminition:: Errors about ``max_length``
If Django gives you an error message saying that ``max_length`` is
not a valid argument, you're most likely using an old version of
Django. (This version of the tutorial is written for the latest
development version of Django.) If you're using a Subversion checkout
of Django's development version (see `the installation docs`_ for
more information), you shouldn't have any problems.
If you want to stick with an older version of Django, you'll want to
switch to `the Django 0.96 tutorial`_, because this tutorial covers
several features that only exist in the Django development version.
.. _the installation docs: ../install/
.. _the Django 0.96 tutorial: ../0.96/tutorial01/
The code is straightforward. Each model is represented by a class that
subclasses ``django.db.models.Model``. Each model has a number of class
variables, each of which represents a database field in the model.
@ -487,6 +503,23 @@ the ``polls/models.py`` file) and adding a ``__unicode__()`` method to both
def __unicode__(self):
return self.choice
.. admonition:: If ``__unicode__()`` doesn't seem to work
If you add the ``__unicode__()`` method to your models and don't
see any change in how they're represented, you're most likely using
an old version of Django. (This version of the tutorial is written
for the latest development version of Django.) If you're using a
Subversion checkout of of Django's development version (see `the
installation docs`_ for more information), you shouldn't have any
problems.
If you want to stick with an older version of Django, you'll want to
switch to `the Django 0.96 tutorial`_, because this tutorial covers
several features that only exist in the Django development version.
.. _the installation docs: ../install/
.. _the Django 0.96 tutorial: ../0.96/tutorial01/
It's important to add ``__unicode__()`` methods to your models, not only for
your own sanity when dealing with the interactive prompt, but also because
objects' representations are used throughout Django's automatically-generated

View File

@ -1,5 +1,8 @@
import unittest
from datetime import timedelta, date
from django.template import Template, Context, add_to_builtins
from django.utils.dateformat import DateFormat
from django.utils.translation import ugettext as _
add_to_builtins('django.contrib.humanize.templatetags.humanize')
@ -8,13 +11,12 @@ class HumanizeTests(unittest.TestCase):
def humanize_tester(self, test_list, result_list, method):
# Using max below ensures we go through both lists
# However, if the lists are not equal length, this raises an exception
for index in xrange(len(max(test_list,result_list))):
for index in xrange(max(len(test_list), len(result_list))):
test_content = test_list[index]
t = Template('{{ test_content|%s }}' % method)
rendered = t.render(Context(locals())).strip()
self.assertEqual(rendered, result_list[index],
msg="""%s test failed, produced %s,
should've produced %s""" % (method, rendered, result_list[index]))
msg="%s test failed, produced %s, should've produced %s" % (method, rendered, result_list[index]))
def test_ordinal(self):
test_list = ('1','2','3','4','11','12',
@ -49,6 +51,20 @@ should've produced %s""" % (method, rendered, result_list[index]))
self.humanize_tester(test_list, result_list, 'apnumber')
def test_naturalday(self):
from django.template import defaultfilters
today = date.today()
yesterday = today - timedelta(days=1)
tomorrow = today + timedelta(days=1)
someday = today - timedelta(days=10)
notdate = u"I'm not a date value"
test_list = (today, yesterday, tomorrow, someday, notdate)
someday_result = defaultfilters.date(someday)
result_list = (_(u'today'), _(u'yesterday'), _(u'tomorrow'),
someday_result, u"I'm not a date value")
self.humanize_tester(test_list, result_list, 'naturalday')
if __name__ == '__main__':
unittest.main()