Fixed #24907 -- Updated contributing tutorial with a more recent example ticket.
This commit is contained in:
parent
7abf418eb1
commit
3fd754f12d
|
@ -97,15 +97,19 @@ The first step to contributing to Django is to get a copy of the source code.
|
||||||
From the command line, use the ``cd`` command to navigate to the directory
|
From the command line, use the ``cd`` command to navigate to the directory
|
||||||
where you'll want your local copy of Django to live.
|
where you'll want your local copy of Django to live.
|
||||||
|
|
||||||
Download the Django source code repository using the following command::
|
Download the Django source code repository using the following command:
|
||||||
|
|
||||||
git clone https://github.com/django/django.git
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ git clone https://github.com/django/django.git
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
For users who wish to use `virtualenv`__, you can use::
|
For users who wish to use `virtualenv`__, you can use:
|
||||||
|
|
||||||
pip install -e /path/to/your/local/clone/django/
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install -e /path/to/your/local/clone/django/
|
||||||
|
|
||||||
(where ``django`` is the directory of your clone that contains
|
(where ``django`` is the directory of your clone that contains
|
||||||
``setup.py``) to link your cloned checkout into a virtual environment. This
|
``setup.py``) to link your cloned checkout into a virtual environment. This
|
||||||
|
@ -117,7 +121,7 @@ __ http://www.virtualenv.org
|
||||||
Rolling back to a previous revision of Django
|
Rolling back to a previous revision of Django
|
||||||
=============================================
|
=============================================
|
||||||
|
|
||||||
For this tutorial, we'll be using ticket :ticket:`17549` as a case study, so we'll
|
For this tutorial, we'll be using ticket :ticket:`24788` as a case study, so we'll
|
||||||
rewind Django's version history in git to before that ticket's patch was
|
rewind Django's version history in git to before that ticket's patch was
|
||||||
applied. This will allow us to go through all of the steps involved in writing
|
applied. This will allow us to go through all of the steps involved in writing
|
||||||
that patch from scratch, including running Django's test suite.
|
that patch from scratch, including running Django's test suite.
|
||||||
|
@ -128,36 +132,46 @@ development revision of Django when working on your own patch for a ticket!**
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
The patch for this ticket was written by Ulrich Petri, and it was applied
|
The patch for this ticket was written by Paweł Marczewski, and it was
|
||||||
to Django as `commit ac2052ebc84c45709ab5f0f25e685bf656ce79bc`__.
|
applied to Django as `commit 4df7e8483b2679fc1cba3410f08960bac6f51115`__.
|
||||||
Consequently, we'll be using the revision of Django just prior to that,
|
Consequently, we'll be using the revision of Django just prior to that,
|
||||||
`commit 39f5bc7fc3a4bb43ed8a1358b17fe0521a1a63ac`__.
|
`commit 4ccfc4439a7add24f8db4ef3960d02ef8ae09887`__.
|
||||||
|
|
||||||
__ https://github.com/django/django/commit/ac2052ebc84c45709ab5f0f25e685bf656ce79bc
|
__ https://github.com/django/django/commit/4df7e8483b2679fc1cba3410f08960bac6f51115
|
||||||
__ https://github.com/django/django/commit/39f5bc7fc3a4bb43ed8a1358b17fe0521a1a63ac
|
__ https://github.com/django/django/commit/4ccfc4439a7add24f8db4ef3960d02ef8ae09887
|
||||||
|
|
||||||
Navigate into Django's root directory (that's the one that contains ``django``,
|
Navigate into Django's root directory (that's the one that contains ``django``,
|
||||||
``docs``, ``tests``, ``AUTHORS``, etc.). You can then check out the older
|
``docs``, ``tests``, ``AUTHORS``, etc.). You can then check out the older
|
||||||
revision of Django that we'll be using in the tutorial below::
|
revision of Django that we'll be using in the tutorial below:
|
||||||
|
|
||||||
git checkout 39f5bc7fc3a4bb43ed8a1358b17fe0521a1a63ac
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887
|
||||||
|
|
||||||
Running Django's test suite for the first time
|
Running Django's test suite for the first time
|
||||||
==============================================
|
==============================================
|
||||||
|
|
||||||
When contributing to Django it's very important that your code changes don't
|
When contributing to Django it's very important that your code changes don't
|
||||||
introduce bugs into other areas of Django. One way to check that Django still
|
introduce bugs into other areas of Django. One way to check that Django still
|
||||||
works after you make your changes is by running Django's test suite. If all
|
works after you make your changes is by running Django's test suite. If all
|
||||||
the tests still pass, then you can be reasonably sure that your changes
|
the tests still pass, then you can be reasonably sure that your changes
|
||||||
haven't completely broken Django. If you've never run Django's test suite
|
haven't completely broken Django. If you've never run Django's test suite
|
||||||
before, it's a good idea to run it once beforehand just to get familiar with
|
before, it's a good idea to run it once beforehand just to get familiar with
|
||||||
what its output is supposed to look like.
|
what its output is supposed to look like.
|
||||||
|
|
||||||
We can run the test suite by simply ``cd``-ing into the Django ``tests/``
|
Before running the test suite, install its dependencies by first ``cd``-ing
|
||||||
directory and, if you're using GNU/Linux, Mac OS X or some other flavor of
|
into the Django ``tests/`` directory and then running:
|
||||||
Unix, run::
|
|
||||||
|
|
||||||
PYTHONPATH=.. python runtests.py --settings=test_sqlite
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install -r requirements/py3.txt # or py2.txt if you are running Python 2
|
||||||
|
|
||||||
|
Now we are ready to run the test suite. If you're using GNU/Linux, Mac OS X or
|
||||||
|
some other flavor of Unix, run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ PYTHONPATH=.. ./runtests.py
|
||||||
|
|
||||||
If you're on Windows, the above should work provided that you are using
|
If you're on Windows, the above should work provided that you are using
|
||||||
"Git Bash" provided by the default Git install. GitHub has a `nice tutorial`__.
|
"Git Bash" provided by the default Git install. GitHub has a `nice tutorial`__.
|
||||||
|
@ -171,7 +185,7 @@ __ https://help.github.com/articles/set-up-git#platform-windows
|
||||||
of ``tests``. ``virtualenv`` puts your copy of Django on the ``PYTHONPATH``
|
of ``tests``. ``virtualenv`` puts your copy of Django on the ``PYTHONPATH``
|
||||||
automatically.
|
automatically.
|
||||||
|
|
||||||
Now sit back and relax. Django's entire test suite has over 4800 different
|
Now sit back and relax. Django's entire test suite has over 9,600 different
|
||||||
tests, so it can take anywhere from 5 to 15 minutes to run, depending on the
|
tests, so it can take anywhere from 5 to 15 minutes to run, depending on the
|
||||||
speed of your computer.
|
speed of your computer.
|
||||||
|
|
||||||
|
@ -234,56 +248,42 @@ Now for our hands-on example.
|
||||||
|
|
||||||
__ http://en.wikipedia.org/wiki/Test-driven_development
|
__ http://en.wikipedia.org/wiki/Test-driven_development
|
||||||
|
|
||||||
Writing some tests for ticket #17549
|
Writing some tests for ticket #24788
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
Ticket :ticket:`17549` describes the following, small feature addition:
|
Ticket :ticket:`24788` proposes a small feature addition: the ability to
|
||||||
|
specify the class level attribute ``prefix`` on Form classes, so that::
|
||||||
|
|
||||||
It's useful for URLField to give you a way to open the URL; otherwise you
|
[…] forms which ship with apps could effectively namespace themselves such
|
||||||
might as well use a CharField.
|
that N overlapping form fields could be POSTed at once and resolved to the
|
||||||
|
correct form.
|
||||||
|
|
||||||
In order to resolve this ticket, we'll add a ``render`` method to the
|
In order to resolve this ticket, we'll add a ``prefix`` attribute to the
|
||||||
``AdminURLFieldWidget`` in order to display a clickable link above the input
|
``BaseForm`` class. When creating instances of this class, passing a prefix to
|
||||||
widget. Before we make those changes though, we're going to write a couple
|
the ``__init__()`` method will still set that prefix on the created instance.
|
||||||
tests to verify that our modification functions correctly and continues to
|
But not passing a prefix (or passing ``None``) will use the class-level prefix.
|
||||||
function correctly in the future.
|
Before we make those changes though, we're going to write a couple tests to
|
||||||
|
verify that our modification functions correctly and continues to function
|
||||||
|
correctly in the future.
|
||||||
|
|
||||||
Navigate to Django's ``tests/regressiontests/admin_widgets/`` folder and
|
Navigate to Django's ``tests/forms_tests/tests/`` folder and open the
|
||||||
open the ``tests.py`` file. Add the following code on line 269 right before the
|
``test_forms.py`` file. Add the following code on line 1674 right before the
|
||||||
``AdminFileWidgetTest`` class::
|
``test_forms_with_null_boolean`` function::
|
||||||
|
|
||||||
class AdminURLWidgetTest(DjangoTestCase):
|
def test_class_prefix(self):
|
||||||
def test_render(self):
|
# Prefix can be also specified at the class level.
|
||||||
w = widgets.AdminURLFieldWidget()
|
class Person(Form):
|
||||||
self.assertHTMLEqual(
|
first_name = CharField()
|
||||||
conditional_escape(w.render('test', '')),
|
prefix = 'foo'
|
||||||
'<input class="vURLField" name="test" type="text" />'
|
|
||||||
)
|
|
||||||
self.assertHTMLEqual(
|
|
||||||
conditional_escape(w.render('test', 'http://example.com')),
|
|
||||||
'<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com" /></p>'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_render_idn(self):
|
p = Person()
|
||||||
w = widgets.AdminURLFieldWidget()
|
self.assertEqual(p.prefix, 'foo')
|
||||||
self.assertHTMLEqual(
|
|
||||||
conditional_escape(w.render('test', 'http://example-äüö.com')),
|
|
||||||
'<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_render_quoting(self):
|
p = Person(prefix='bar')
|
||||||
w = widgets.AdminURLFieldWidget()
|
self.assertEqual(p.prefix, 'bar')
|
||||||
self.assertHTMLEqual(
|
|
||||||
conditional_escape(w.render('test', 'http://example.com/<sometag>some text</sometag>')),
|
|
||||||
'<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>'
|
|
||||||
)
|
|
||||||
self.assertHTMLEqual(
|
|
||||||
conditional_escape(w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>')),
|
|
||||||
'<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
|
|
||||||
)
|
|
||||||
|
|
||||||
The new tests check to see that the ``render`` method we'll be adding works
|
This new test checks that setting a class level prefix works as expected, and
|
||||||
correctly in a couple different situations.
|
that passing a ``prefix`` parameter when creating an instance still works too.
|
||||||
|
|
||||||
.. admonition:: But this testing thing looks kinda hard...
|
.. admonition:: But this testing thing looks kinda hard...
|
||||||
|
|
||||||
|
@ -304,68 +304,67 @@ __ https://docs.python.org/library/unittest.html
|
||||||
Running your new test
|
Running your new test
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Remember that we haven't actually made any modifications to
|
Remember that we haven't actually made any modifications to ``BaseForm`` yet,
|
||||||
``AdminURLFieldWidget`` yet, so our tests are going to fail. Let's run all the
|
so our tests are going to fail. Let's run all the tests in the ``forms_tests``
|
||||||
tests in the ``model_forms_regress`` folder to make sure that's really what
|
folder to make sure that's really what happens. From the command line, ``cd``
|
||||||
happens. From the command line, ``cd`` into the Django ``tests/`` directory
|
into the Django ``tests/`` directory and run:
|
||||||
and run::
|
|
||||||
|
|
||||||
PYTHONPATH=.. python runtests.py --settings=test_sqlite admin_widgets
|
.. code-block:: console
|
||||||
|
|
||||||
If the tests ran correctly, you should see three failures corresponding to each
|
$ PYTHONPATH=.. ./runtests.py forms_tests
|
||||||
of the test methods we added. If all of the tests passed, then you'll want to
|
|
||||||
make sure that you added the new test shown above to the appropriate folder and
|
If the tests ran correctly, you should see one failure corresponding to the test
|
||||||
class.
|
method we added. If all of the tests passed, then you'll want to make sure that
|
||||||
|
you added the new test shown above to the appropriate folder and class.
|
||||||
|
|
||||||
Writing the code for your ticket
|
Writing the code for your ticket
|
||||||
================================
|
================================
|
||||||
|
|
||||||
Next we'll be adding the functionality described in ticket :ticket:`17549` to
|
Next we'll be adding the functionality described in ticket :ticket:`24788` to
|
||||||
Django.
|
Django.
|
||||||
|
|
||||||
Writing the code for ticket #17549
|
Writing the code for ticket #24788
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
Navigate to the ``django/django/contrib/admin/`` folder and open the
|
Navigate to the ``django/django/forms/`` folder and open the ``forms.py`` file.
|
||||||
``widgets.py`` file. Find the ``AdminURLFieldWidget`` class on line 302 and add
|
Find the ``BaseForm`` class on line 72 and add the ``prefix`` class attribute
|
||||||
the following ``render`` method after the existing ``__init__`` method::
|
right after the ``field_order`` attribute::
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
class BaseForm(object):
|
||||||
html = super(AdminURLFieldWidget, self).render(name, value, attrs)
|
# This is the main implementation of all the Form logic. Note that this
|
||||||
if value:
|
# class is different than Form. See the comments by the Form class for more
|
||||||
value = force_text(self._format_value(value))
|
# information. Any improvements to the form API should be made to *this*
|
||||||
final_attrs = {'href': mark_safe(smart_urlquote(value))}
|
# class, not to the Form class.
|
||||||
html = format_html(
|
field_order = None
|
||||||
'<p class="url">{} <a {}>{}</a><br />{} {}</p>',
|
prefix = None
|
||||||
_('Currently:'), flatatt(final_attrs), value,
|
|
||||||
_('Change:'), html
|
|
||||||
)
|
|
||||||
return html
|
|
||||||
|
|
||||||
Verifying your test now passes
|
Verifying your test now passes
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
Once you're done modifying Django, we need to make sure that the tests we wrote
|
Once you're done modifying Django, we need to make sure that the tests we wrote
|
||||||
earlier pass, so we can see whether the code we wrote above is working
|
earlier pass, so we can see whether the code we wrote above is working
|
||||||
correctly. To run the tests in the ``admin_widgets`` folder, ``cd`` into the
|
correctly. To run the tests in the ``forms_tests`` folder, ``cd`` into the
|
||||||
Django ``tests/`` directory and run::
|
Django ``tests/`` directory and run:
|
||||||
|
|
||||||
PYTHONPATH=.. python runtests.py --settings=test_sqlite admin_widgets
|
.. code-block:: console
|
||||||
|
|
||||||
Oops, good thing we wrote those tests! You should still see 3 failures with
|
$ PYTHONPATH=.. ./runtests.py forms_tests
|
||||||
|
|
||||||
|
Oops, good thing we wrote those tests! You should still see one failure with
|
||||||
the following exception::
|
the following exception::
|
||||||
|
|
||||||
NameError: global name 'smart_urlquote' is not defined
|
AssertionError: None != 'foo'
|
||||||
|
|
||||||
We forgot to add the import for that method. Go ahead and add the
|
We forgot to add the conditional statement in the ``__init__`` method. Go ahead
|
||||||
``smart_urlquote`` import at the end of line 13 of
|
and change ``self.prefix = prefix`` that is now on line 87 of
|
||||||
``django/contrib/admin/widgets.py`` so it looks as follows::
|
``django/forms/forms.py``, adding a conditional statement::
|
||||||
|
|
||||||
from django.utils.html import escape, format_html, format_html_join, smart_urlquote
|
if prefix is not None:
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
Re-run the tests and everything should pass. If it doesn't, make sure you
|
Re-run the tests and everything should pass. If it doesn't, make sure you
|
||||||
correctly modified the ``AdminURLFieldWidget`` class as shown above and
|
correctly modified the ``BaseForm`` class as shown above and copied the new test
|
||||||
copied the new tests correctly.
|
correctly.
|
||||||
|
|
||||||
Running Django's test suite for the second time
|
Running Django's test suite for the second time
|
||||||
===============================================
|
===============================================
|
||||||
|
@ -377,27 +376,36 @@ passing the entire test suite doesn't guarantee your code is bug free, it does
|
||||||
help identify many bugs and regressions that might otherwise go unnoticed.
|
help identify many bugs and regressions that might otherwise go unnoticed.
|
||||||
|
|
||||||
To run the entire Django test suite, ``cd`` into the Django ``tests/``
|
To run the entire Django test suite, ``cd`` into the Django ``tests/``
|
||||||
directory and run::
|
directory and run:
|
||||||
|
|
||||||
PYTHONPATH=.. python runtests.py --settings=test_sqlite
|
.. code-block:: console
|
||||||
|
|
||||||
As long as you don't see any failures, you're good to go. Note that this fix
|
$ PYTHONPATH=.. ./runtests.py
|
||||||
also made a `small CSS change`__ to format the new widget. You can make the
|
|
||||||
change if you'd like, but we'll skip it for now in the interest of brevity.
|
|
||||||
|
|
||||||
__ https://github.com/django/django/commit/ac2052ebc84c45709ab5f0f25e685bf656ce79bc#diff-0
|
As long as you don't see any failures, you're good to go.
|
||||||
|
|
||||||
Writing Documentation
|
Writing Documentation
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
This is a new feature, so it should be documented. Add the following on line
|
This is a new feature, so it should be documented. Add the following section on
|
||||||
925 of ``django/docs/ref/models/fields.txt`` beneath the existing docs for
|
line 1068 (at the end of the file) of ``django/docs/ref/forms/api.txt``::
|
||||||
``URLField``::
|
|
||||||
|
|
||||||
.. versionadded:: 1.5
|
The prefix can also be specified on the form class::
|
||||||
|
|
||||||
The current value of the field will be displayed as a clickable link above the
|
>>> class PersonForm(forms.Form):
|
||||||
input widget.
|
... ...
|
||||||
|
... prefix = 'person'
|
||||||
|
|
||||||
|
.. versionadded:: 1.9
|
||||||
|
|
||||||
|
The ability to specify ``prefix`` on the form class was added.
|
||||||
|
|
||||||
|
Since this new feature will be in an upcoming release it is also added to the
|
||||||
|
release notes for Django 1.9, on line 164 under the "Forms" section in the file
|
||||||
|
``docs/releases/1.9.txt``::
|
||||||
|
|
||||||
|
* A form prefix can be specified inside a form class, not only when
|
||||||
|
instantiating a form. See :ref:`form-prefix` for details.
|
||||||
|
|
||||||
For more information on writing documentation, including an explanation of what
|
For more information on writing documentation, including an explanation of what
|
||||||
the ``versionadded`` bit is all about, see
|
the ``versionadded`` bit is all about, see
|
||||||
|
@ -410,113 +418,106 @@ Generating a patch for your changes
|
||||||
|
|
||||||
Now it's time to generate a patch file that can be uploaded to Trac or applied
|
Now it's time to generate a patch file that can be uploaded to Trac or applied
|
||||||
to another copy of Django. To get a look at the content of your patch, run the
|
to another copy of Django. To get a look at the content of your patch, run the
|
||||||
following command::
|
following command:
|
||||||
|
|
||||||
git diff
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ git diff
|
||||||
|
|
||||||
This will display the differences between your current copy of Django (with
|
This will display the differences between your current copy of Django (with
|
||||||
your changes) and the revision that you initially checked out earlier in the
|
your changes) and the revision that you initially checked out earlier in the
|
||||||
tutorial.
|
tutorial.
|
||||||
|
|
||||||
Once you're done looking at the patch, hit the ``q`` key to exit back to the
|
Once you're done looking at the patch, hit the ``q`` key to exit back to the
|
||||||
command line. If the patch's content looked okay, you can run the following
|
command line. If the patch's content looked okay, you can run the following
|
||||||
command to save the patch file to your current working directory::
|
command to save the patch file to your current working directory:
|
||||||
|
|
||||||
git diff > 17549.diff
|
.. code-block:: console
|
||||||
|
|
||||||
You should now have a file in the root Django directory called ``17549.diff``.
|
$ git diff > 24788.diff
|
||||||
|
|
||||||
|
You should now have a file in the root Django directory called ``24788.diff``.
|
||||||
This patch file contains all your changes and should look this:
|
This patch file contains all your changes and should look this:
|
||||||
|
|
||||||
.. code-block:: diff
|
.. code-block:: diff
|
||||||
|
|
||||||
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
|
diff --git a/django/forms/forms.py b/django/forms/forms.py
|
||||||
index 1e0bc2d..9e43a10 100644
|
index 509709f..d1370de 100644
|
||||||
--- a/django/contrib/admin/widgets.py
|
--- a/django/forms/forms.py
|
||||||
+++ b/django/contrib/admin/widgets.py
|
+++ b/django/forms/forms.py
|
||||||
@@ -10,7 +10,7 @@ from django.contrib.admin.templatetags.admin_static import static
|
@@ -75,6 +75,7 @@ class BaseForm(object):
|
||||||
from django.core.urlresolvers import reverse
|
# information. Any improvements to the form API should be made to *this*
|
||||||
from django.forms.widgets import RadioFieldRenderer
|
# class, not to the Form class.
|
||||||
from django.forms.util import flatatt
|
field_order = None
|
||||||
-from django.utils.html import escape, format_html, format_html_join
|
+ prefix = None
|
||||||
+from django.utils.html import escape, format_html, format_html_join, smart_urlquote
|
|
||||||
from django.utils.text import Truncator
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
@@ -306,6 +306,18 @@ class AdminURLFieldWidget(forms.TextInput):
|
|
||||||
final_attrs.update(attrs)
|
|
||||||
super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
|
|
||||||
|
|
||||||
+ def render(self, name, value, attrs=None):
|
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
|
||||||
+ html = super(AdminURLFieldWidget, self).render(name, value, attrs)
|
initial=None, error_class=ErrorList, label_suffix=None,
|
||||||
+ if value:
|
@@ -83,7 +84,8 @@ class BaseForm(object):
|
||||||
+ value = force_text(self._format_value(value))
|
self.data = data or {}
|
||||||
+ final_attrs = {'href': mark_safe(smart_urlquote(value))}
|
self.files = files or {}
|
||||||
+ html = format_html(
|
self.auto_id = auto_id
|
||||||
+ '<p class="url">{} <a {}>{}</a><br />{} {}</p>',
|
- self.prefix = prefix
|
||||||
+ _('Currently:'), flatatt(final_attrs), value,
|
+ if prefix is not None:
|
||||||
+ _('Change:'), html
|
+ self.prefix = prefix
|
||||||
+ )
|
self.initial = initial or {}
|
||||||
+ return html
|
self.error_class = error_class
|
||||||
|
# Translators: This is the default suffix added to form field labels
|
||||||
|
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
|
||||||
|
index 3bc39cd..008170d 100644
|
||||||
|
--- a/docs/ref/forms/api.txt
|
||||||
|
+++ b/docs/ref/forms/api.txt
|
||||||
|
@@ -1065,3 +1065,13 @@ You can put several Django forms inside one ``<form>`` tag. To give each
|
||||||
|
>>> print(father.as_ul())
|
||||||
|
<li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li>
|
||||||
|
<li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li>
|
||||||
+
|
+
|
||||||
class AdminIntegerFieldWidget(forms.TextInput):
|
+The prefix can also be specified on the form class::
|
||||||
class_name = 'vIntegerField'
|
|
||||||
|
|
||||||
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
|
|
||||||
index 809d56e..d44f85f 100644
|
|
||||||
--- a/docs/ref/models/fields.txt
|
|
||||||
+++ b/docs/ref/models/fields.txt
|
|
||||||
@@ -922,6 +922,10 @@ Like all :class:`CharField` subclasses, :class:`URLField` takes the optional
|
|
||||||
:attr:`~CharField.max_length`argument. If you don't specify
|
|
||||||
:attr:`~CharField.max_length`, a default of 200 is used.
|
|
||||||
|
|
||||||
+.. versionadded:: 1.5
|
|
||||||
+
|
+
|
||||||
+The current value of the field will be displayed as a clickable link above the
|
+ >>> class PersonForm(forms.Form):
|
||||||
+input widget.
|
+ ... ...
|
||||||
|
+ ... prefix = 'person'
|
||||||
Relationship fields
|
|
||||||
===================
|
|
||||||
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
|
|
||||||
index 4b11543..94acc6d 100644
|
|
||||||
--- a/tests/regressiontests/admin_widgets/tests.py
|
|
||||||
+++ b/tests/regressiontests/admin_widgets/tests.py
|
|
||||||
|
|
||||||
@@ -265,6 +265,35 @@ class AdminSplitDateTimeWidgetTest(DjangoTestCase):
|
|
||||||
'<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>',
|
|
||||||
)
|
|
||||||
|
|
||||||
+class AdminURLWidgetTest(DjangoTestCase):
|
|
||||||
+ def test_render(self):
|
|
||||||
+ w = widgets.AdminURLFieldWidget()
|
|
||||||
+ self.assertHTMLEqual(
|
|
||||||
+ conditional_escape(w.render('test', '')),
|
|
||||||
+ '<input class="vURLField" name="test" type="text" />'
|
|
||||||
+ )
|
|
||||||
+ self.assertHTMLEqual(
|
|
||||||
+ conditional_escape(w.render('test', 'http://example.com')),
|
|
||||||
+ '<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com" /></p>'
|
|
||||||
+ )
|
|
||||||
+
|
+
|
||||||
+ def test_render_idn(self):
|
+.. versionadded:: 1.9
|
||||||
+ w = widgets.AdminURLFieldWidget()
|
|
||||||
+ self.assertHTMLEqual(
|
|
||||||
+ conditional_escape(w.render('test', 'http://example-äüö.com')),
|
|
||||||
+ '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>'
|
|
||||||
+ )
|
|
||||||
+
|
+
|
||||||
+ def test_render_quoting(self):
|
+ The ability to specify ``prefix`` on the form class was added.
|
||||||
+ w = widgets.AdminURLFieldWidget()
|
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
|
||||||
+ self.assertHTMLEqual(
|
index 5b58f79..f9bb9de 100644
|
||||||
+ conditional_escape(w.render('test', 'http://example.com/<sometag>some text</sometag>')),
|
--- a/docs/releases/1.9.txt
|
||||||
+ '<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>'
|
+++ b/docs/releases/1.9.txt
|
||||||
+ )
|
@@ -161,6 +161,9 @@ Forms
|
||||||
+ self.assertHTMLEqual(
|
:attr:`~django.forms.Form.field_order` attribute, the ``field_order``
|
||||||
+ conditional_escape(w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>')),
|
constructor argument , or the :meth:`~django.forms.Form.order_fields` method.
|
||||||
+ '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
|
|
||||||
+ )
|
|
||||||
|
|
||||||
class AdminFileWidgetTest(DjangoTestCase):
|
+* A form prefix can be specified inside a form class, not only when
|
||||||
def test_render(self):
|
+ instantiating a form. See :ref:`form-prefix` for details.
|
||||||
|
+
|
||||||
|
Generic Views
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
|
||||||
|
index 690f205..e07fae2 100644
|
||||||
|
--- a/tests/forms_tests/tests/test_forms.py
|
||||||
|
+++ b/tests/forms_tests/tests/test_forms.py
|
||||||
|
@@ -1671,6 +1671,18 @@ class FormsTestCase(SimpleTestCase):
|
||||||
|
self.assertEqual(p.cleaned_data['last_name'], 'Lennon')
|
||||||
|
self.assertEqual(p.cleaned_data['birthday'], datetime.date(1940, 10, 9))
|
||||||
|
|
||||||
|
+ def test_class_prefix(self):
|
||||||
|
+ # Prefix can be also specified at the class level.
|
||||||
|
+ class Person(Form):
|
||||||
|
+ first_name = CharField()
|
||||||
|
+ prefix = 'foo'
|
||||||
|
+
|
||||||
|
+ p = Person()
|
||||||
|
+ self.assertEqual(p.prefix, 'foo')
|
||||||
|
+
|
||||||
|
+ p = Person(prefix='bar')
|
||||||
|
+ self.assertEqual(p.prefix, 'bar')
|
||||||
|
+
|
||||||
|
def test_forms_with_null_boolean(self):
|
||||||
|
# NullBooleanField is a bit of a special case because its presentation (widget)
|
||||||
|
# is different than its data. This is handled transparently, though.
|
||||||
|
|
||||||
So what do I do next?
|
So what do I do next?
|
||||||
=====================
|
=====================
|
||||||
|
@ -529,10 +530,12 @@ oriented workflow </internals/contributing/writing-code/working-with-git>` is
|
||||||
recommended.
|
recommended.
|
||||||
|
|
||||||
Since we never committed our changes locally, perform the following to get your
|
Since we never committed our changes locally, perform the following to get your
|
||||||
git branch back to a good starting point::
|
git branch back to a good starting point:
|
||||||
|
|
||||||
git reset --hard HEAD
|
.. code-block:: console
|
||||||
git checkout master
|
|
||||||
|
$ git reset --hard HEAD
|
||||||
|
$ git checkout master
|
||||||
|
|
||||||
More information for new contributors
|
More information for new contributors
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
@ -561,7 +564,7 @@ Finding your first real ticket
|
||||||
Once you've looked through some of that information, you'll be ready to go out
|
Once you've looked through some of that information, you'll be ready to go out
|
||||||
and find a ticket of your own to write a patch for. Pay special attention to
|
and find a ticket of your own to write a patch for. Pay special attention to
|
||||||
tickets with the "easy pickings" criterion. These tickets are often much
|
tickets with the "easy pickings" criterion. These tickets are often much
|
||||||
simpler in nature and are great for first time contributors. Once you're
|
simpler in nature and are great for first time contributors. Once you're
|
||||||
familiar with contributing to Django, you can move on to writing patches for
|
familiar with contributing to Django, you can move on to writing patches for
|
||||||
more difficult and complicated tickets.
|
more difficult and complicated tickets.
|
||||||
|
|
||||||
|
|
|
@ -455,6 +455,7 @@ makemessages
|
||||||
makemigrations
|
makemigrations
|
||||||
Mako
|
Mako
|
||||||
Mapnik
|
Mapnik
|
||||||
|
Marczewski
|
||||||
Marino
|
Marino
|
||||||
Markus
|
Markus
|
||||||
MBR
|
MBR
|
||||||
|
@ -545,6 +546,7 @@ Palau
|
||||||
parameterized
|
parameterized
|
||||||
params
|
params
|
||||||
parens
|
parens
|
||||||
|
Paweł
|
||||||
pdf
|
pdf
|
||||||
PEM
|
PEM
|
||||||
perl
|
perl
|
||||||
|
|
Loading…
Reference in New Issue