| | 1 | =================================== |
| | 2 | Writing your first patch for Django |
| | 3 | =================================== |
| | 4 | |
| | 5 | Introduction |
| | 6 | ============ |
| | 7 | |
| | 8 | Interested in giving back to the community a little? Maybe you've found a bug |
| | 9 | in Django that you'd like to see fixed, or maybe there's a small feature you |
| | 10 | want added. |
| | 11 | |
| | 12 | Contributing back to Django itself is the best way to see your own concerns |
| | 13 | addressed. This may seem daunting at first, but it's really pretty simple. |
| | 14 | We'll walk you through the entire process, so you can learn by example. |
| | 15 | |
| | 16 | Who's this tutorial for? |
| | 17 | ------------------------ |
| | 18 | |
| | 19 | For this tutorial, we expect that you have at least a basic understanding of |
| | 20 | how Django works. This means you should be comfortable going through the |
| | 21 | existing tutorials on :doc:`writing your first Django app</intro/tutorial01>`. |
| | 22 | In addition, you should have a good understanding of Python itself. But if you |
| | 23 | don't, `Dive Into Python`__ is a fantastic (and free) online book for beginning |
| | 24 | Python programmers. |
| | 25 | |
| | 26 | Those of you who are unfamiliar with version control systems and Trac will find |
| | 27 | that this tutorial and its links include just enough information to get started. |
| | 28 | However, you'll probably want to read some more about these different tools if |
| | 29 | you plan on contributing to Django regularly. |
| | 30 | |
| | 31 | For the most part though, this tutorial tries to explain as much as possible, |
| | 32 | so that it can be of use to the widest audience. |
| | 33 | |
| | 34 | .. admonition:: Where to get help: |
| | 35 | |
| | 36 | If you're having trouble going through this tutorial, please post a message |
| | 37 | to `django-users`__ or drop by `#django on irc.freenode.net`__ to chat |
| | 38 | with other Django users who might be able to help. |
| | 39 | |
| | 40 | __ http://diveintopython.net/toc/index.html |
| | 41 | __ http://groups.google.com/group/django-users |
| | 42 | __ irc://irc.freenode.net/django |
| | 43 | |
| | 44 | What does this tutorial cover? |
| | 45 | ------------------------------ |
| | 46 | |
| | 47 | We'll be walking you through contributing a patch to Django for the first time. |
| | 48 | By the end of this tutorial, you should have a basic understanding of both the |
| | 49 | tools and the processes involved. Specifically, we'll be covering the following: |
| | 50 | |
| | 51 | * Installing Git. |
| | 52 | * How to download a development copy of Django. |
| | 53 | * Running Django's test suite. |
| | 54 | * Writing a test for your patch. |
| | 55 | * Writing the code for your patch. |
| | 56 | * Testing your patch. |
| | 57 | * Generating a patch file for your changes. |
| | 58 | * Where to look for more information. |
| | 59 | |
| | 60 | Once you're done with the tutorial, you can look through the rest of |
| | 61 | :doc:`Django's documentation on contributing</internals/contributing/index>`. |
| | 62 | It contains lots of great information and is a must read for anyone who'd like |
| | 63 | to become a regular contributor to Django. If you've got questions, it's |
| | 64 | probably got the answers. |
| | 65 | |
| | 66 | Installing Git |
| | 67 | ============== |
| | 68 | |
| | 69 | For this tutorial, you'll need Git installed to download the current |
| | 70 | development version of Django and to generate patch files for the changes you |
| | 71 | make. |
| | 72 | |
| | 73 | To check whether or not you have Git installed, enter ``git`` into the command |
| | 74 | line. If you get messages saying that this command could be found, you'll have |
| | 75 | to download and install it, see `Git's download page`__. |
| | 76 | |
| | 77 | .. note:: |
| | 78 | |
| | 79 | If you're not that familiar with Git, you can always find out more about |
| | 80 | its commands (once it's installed) by typing ``git help`` into the command |
| | 81 | line. |
| | 82 | |
| | 83 | __ http://git-scm.com/download |
| | 84 | |
| | 85 | Getting a copy of Django's development version |
| | 86 | ============================================== |
| | 87 | |
| | 88 | The first step to contributing to Django is to get a copy of the source code. |
| | 89 | If you're using ``virtualenv``, it's better if you don't use ``pip`` to install |
| | 90 | Django, since that can cause Django's setup.py script to be run. Follow the |
| | 91 | instructions below to use Git to checkout a copy of Django manually. |
| | 92 | |
| | 93 | From the command line, use the ``cd`` command to navigate to the directory |
| | 94 | where you'll want your local copy of Django to live. |
| | 95 | |
| | 96 | Download the Django source code repository using the following command:: |
| | 97 | |
| | 98 | git clone https://github.com/django/django.git |
| | 99 | |
| | 100 | Rolling back to a previous revision of Django |
| | 101 | ============================================= |
| | 102 | |
| | 103 | For this tutorial, we'll be using `ticket #15315`__ as a case study, so we'll |
| | 104 | be using an older revision of Django from before that ticket's patch was |
| | 105 | applied. This will allow us to go through all of the steps involved in writing |
| | 106 | that patch from scratch, including running Django's test suite. |
| | 107 | |
| | 108 | **Keep in mind that while we'll be using an older revision of Django's trunk |
| | 109 | for the purposes of the tutorial below, you should always use the current |
| | 110 | development revision of Django when working on your own patch for a ticket!** |
| | 111 | |
| | 112 | .. note:: |
| | 113 | |
| | 114 | The patch for this ticket was generously written by SardarNL and Will Hardy, |
| | 115 | and it was applied to Django as `changeset 16659`__. Consequently, we'll be |
| | 116 | using the revision of Django just prior to that, revision 16658 (this is |
| | 117 | from when Django used Subversion rather than Git for source control). |
| | 118 | |
| | 119 | __ https://code.djangoproject.com/ticket/15315 |
| | 120 | __ https://code.djangoproject.com/changeset/16659 |
| | 121 | |
| | 122 | Navigate into Django's root directory (that's the one that contains ``django``, |
| | 123 | ``docs``, ``tests``, ``AUTHORS``, etc.). You can then check out the older |
| | 124 | revision of Django that we'll be using in the tutorial below:: |
| | 125 | |
| | 126 | git checkout 1f770c62da69866835bb8fc9355128b6b493a97e |
| | 127 | |
| | 128 | Running Django's test suite for the first time |
| | 129 | ============================================== |
| | 130 | |
| | 131 | When contributing to Django it's very important that your code changes don't |
| | 132 | introduce bugs into other areas of Django. One way to check that Django stills |
| | 133 | works after you make your changes is by running Django's test suite. If all |
| | 134 | the tests still pass, then you can be reasonably sure that your changes |
| | 135 | haven't completely broken Django. If you've never run Django's test suite |
| | 136 | before, it's a good idea to run it once beforehand just to get familiar with |
| | 137 | what its output is supposed to look like. |
| | 138 | |
| | 139 | |
| | 140 | Setting Django up to run the test suite |
| | 141 | --------------------------------------- |
| | 142 | |
| | 143 | Before we can actually run the test suite though, we need to make sure that |
| | 144 | your new local copy of Django is on your ``PYTHONPATH``; otherwise, the test |
| | 145 | suite won't run properly. We also need to make sure that there aren't any |
| | 146 | **other** copies of Django installed somewhere else that are taking priority |
| | 147 | over your newer copy (this happens more often than you might think). To check |
| | 148 | for these problems, start up the Python interpreter and follow along with the |
| | 149 | code below:: |
| | 150 | |
| | 151 | >>> import django |
| | 152 | >>> django |
| | 153 | <module 'django' from '/.../django/__init__.pyc'> |
| | 154 | |
| | 155 | If you get an ``ImportError: No module named django`` after entering the first |
| | 156 | line, then you'll need to add your new copy of Django to your ``PYTHONPATH``. |
| | 157 | |
| | 158 | If you didn't get any errors, then look at the path found in the third line |
| | 159 | (abbreviated above as ``/.../django/__init__.pyc``). If that isn't the |
| | 160 | directory that you put Django into earlier in this tutorial, then there is |
| | 161 | **another** copy of Django on your ``PYTHONPATH`` that is taking priority over |
| | 162 | the newer copy. You'll either have to remove this older copy from your |
| | 163 | ``PYTHONPATH``, or add your new copy to the beginning of your ``PYTHONPATH`` |
| | 164 | so that it takes priority. |
| | 165 | |
| | 166 | .. note:: |
| | 167 | |
| | 168 | If you're a savvy Djangonaut you might be thinking that using ``virtualenv`` |
| | 169 | would work perfectly for this type of thing, and you'd be right. Using |
| | 170 | ``virtualenv`` with the ``--no-site-packages`` option isolates your local |
| | 171 | copy of Django from the rest of your system and avoids potential conflicts. |
| | 172 | |
| | 173 | Make sure you don't use ``pip`` to install Django, since that causes |
| | 174 | Django's setup.py script to be run. If you do, you'll lose Django's root |
| | 175 | directory and end up with only the code installed. So you'll still need to |
| | 176 | use Git to check out a copy of Django, and then add it to your |
| | 177 | ``PYTHONPATH``. |
| | 178 | |
| | 179 | Running the full test suite |
| | 180 | --------------------------- |
| | 181 | |
| | 182 | Once Django is setup properly, we can actually run the test suite. Simply |
| | 183 | ``cd`` into the Django ``tests/`` directory and run: |
| | 184 | |
| | 185 | .. code-block:: bash |
| | 186 | |
| | 187 | ./runtests.py --settings=test_sqlite |
| | 188 | |
| | 189 | If you get an ``ImportError: No module named django.contrib`` error, you still |
| | 190 | need to add your current copy of Django to your ``PYTHONPATH``. |
| | 191 | |
| | 192 | Otherwise, sit back and relax. Django's entire test suite has over 4000 |
| | 193 | different tests, so it can take anywhere from 5 to 15 minutes to run, |
| | 194 | depending on the speed of your computer. |
| | 195 | |
| | 196 | .. note:: |
| | 197 | |
| | 198 | While Django's test suite is running, you'll see a stream of characters |
| | 199 | representing the status of each test as it's run. ``E`` indicates that an |
| | 200 | error was raised during a test, and ``F`` indicates that a test's |
| | 201 | assertions failed. Both of these are considered to be test failures. |
| | 202 | Meanwhile, ``x`` and ``s`` indicated expected failures and skipped tests, |
| | 203 | respectively. Dots indicate passing tests. |
| | 204 | |
| | 205 | Once the process is done, you should be greeted with a message informing you |
| | 206 | whether the test suite passed or failed. Since you haven't yet made any changes |
| | 207 | to Django's code, the entire test suite **should** pass, however, the latest |
| | 208 | Django trunk may not always be stable. In this case you might run into a couple |
| | 209 | of failures that were later fixed: |
| | 210 | |
| | 211 | * ``test_week_view_allow_future |
| | 212 | (regressiontests.generic_views.dates.WeekArchiveViewTests)``: This test |
| | 213 | happened to pass in 2011 when it was added because January 1, 2012 is a |
| | 214 | Sunday. It was fixed to pass regardless of the year in which it was run |
| | 215 | in `[b9910bdd]`__. |
| | 216 | |
| | 217 | * ``int() argument must be a string or a number, not 'list'`` and |
| | 218 | ``"ManagementForm data is missing or has been tampered with`` errors in the |
| | 219 | ``contrib.formtools`` tests: These tests sometimes failed if you had a buggy |
| | 220 | version of ``simplejson`` (2.0.9 in particular). This was fixed in |
| | 221 | `[358e5a80]`__. |
| | 222 | |
| | 223 | __ https://github.com/django/django/commit/b9910bdd/ |
| | 224 | __ https://github.com/django/django/commit/358e5a80/ |
| | 225 | |
| | 226 | If you get failures or errors besides what's described above, make sure you've |
| | 227 | followed all of the previous steps properly. See :ref:`running-unit-tests` for |
| | 228 | more information. |
| | 229 | |
| | 230 | Writing a test for your ticket |
| | 231 | ============================== |
| | 232 | |
| | 233 | In most cases, for a patch to be accepted into Django it has to include tests. |
| | 234 | For bug fix patches, this means writing a regression test to ensure that the |
| | 235 | bug is never reintroduced into Django later on. A regression test should be |
| | 236 | written in such a way that it will fail while the bug still exists and pass |
| | 237 | once the bug has been fixed. For patches containing new features, you'll need |
| | 238 | to include tests which ensure that the new features are working correctly. |
| | 239 | They too should fail when the new feature is not present, and then pass once it |
| | 240 | has been implemented. |
| | 241 | |
| | 242 | A good way to do this is to write your new tests first, before making any |
| | 243 | changes to the code. This style of development is called |
| | 244 | `test-driven development`__ and can be applied to both entire projects and |
| | 245 | single patches. After writing your tests, you then run them to make sure that |
| | 246 | they do indeed fail (since you haven't fixed that bug or added that feature |
| | 247 | yet). If your new tests don't fail, you'll need to fix them so that they do. |
| | 248 | After all, a regression test that passes regardless of whether a bug is present |
| | 249 | is not very helpful at preventing that bug from reoccurring down the road. |
| | 250 | |
| | 251 | Now for our hands on example. |
| | 252 | |
| | 253 | __ http://en.wikipedia.org/wiki/Test-driven_development |
| | 254 | |
| | 255 | Writing a test for ticket #15315 |
| | 256 | -------------------------------- |
| | 257 | |
| | 258 | `Ticket #15315`__ describes the following, small feature addition: |
| | 259 | |
| | 260 | Since Django 1.2, the ``ModelForm`` class can override widgets by |
| | 261 | specifying a ``'widgets'`` attribute in ``Meta``, similar to ``'fields'`` |
| | 262 | or ``'exclude'``. The ``modelform_factory`` function doesn't have any way |
| | 263 | to specify widgets directly, so the only option is to define a new |
| | 264 | ``ModelForm`` subclass with a ``'widget'`` attribute in ``Meta``, and then |
| | 265 | pass that class to ``modelform_factory`` using the ``'form'`` argument. |
| | 266 | This is more complex than it needs to be. |
| | 267 | |
| | 268 | The fix is to add a new keyword argument ``widgets=None`` to |
| | 269 | ``modelform_factory``. |
| | 270 | |
| | 271 | In order to resolve this ticket, we'll modify the ``modelform_factory`` |
| | 272 | function to accept a ``widgets`` keyword argument. Before we make those changes |
| | 273 | though, we're going to write a test to verify that our modification functions |
| | 274 | correctly and continues to function correctly in the future. |
| | 275 | |
| | 276 | Navigate to Django's ``tests/regressiontests/model_forms_regress/`` folder and |
| | 277 | open the ``tests.py`` file. Find the :class:`FormFieldCallbackTests` class on |
| | 278 | line 264 and add the ``test_factory_with_widget_argument`` test to it as shown |
| | 279 | below:: |
| | 280 | |
| | 281 | class FormFieldCallbackTests(TestCase): |
| | 282 | |
| | 283 | def test_factory_with_widget_argument(self): |
| | 284 | """ Regression for #15315: modelform_factory should accept widgets |
| | 285 | argument |
| | 286 | """ |
| | 287 | widget = forms.Textarea() |
| | 288 | |
| | 289 | # Without the widget, the form field should not have the widget set to a textarea |
| | 290 | Form = modelform_factory(Person) |
| | 291 | self.assertNotEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) |
| | 292 | |
| | 293 | # With a widget, the form field should have the widget set to a textarea |
| | 294 | Form = modelform_factory(Person, widgets={'name':widget}) |
| | 295 | self.assertEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) |
| | 296 | |
| | 297 | def test_baseform_with_widgets_in_meta(self): |
| | 298 | ... |
| | 299 | |
| | 300 | This test checks to see if the function ``modelform_factory`` accepts the new |
| | 301 | widgets argument specifying what widgets to use for each field. It also makes |
| | 302 | sure that those form fields are using the specified widgets. Now we have the |
| | 303 | test for our patch written. |
| | 304 | |
| | 305 | .. admonition:: But this testing thing looks kinda hard... |
| | 306 | |
| | 307 | If you've never had to deal with tests before, they can look a little hard |
| | 308 | to write at first glance. Fortunately, testing is a *very* big subject in |
| | 309 | computer programming, so there's lots of information out there: |
| | 310 | |
| | 311 | * A good first look at writing tests for Django can be found in the |
| | 312 | documentation on :doc:`Testing Django applications</topics/testing/>`. |
| | 313 | * Dive Into Python (a free online book for beginning Python developers) |
| | 314 | includes a great `introduction to Unit Testing`__. |
| | 315 | * After reading those, if you want something a little meatier to sink |
| | 316 | your teeth into, there's always the `Python unittest documentation`__. |
| | 317 | |
| | 318 | __ https://code.djangoproject.com/ticket/15315 |
| | 319 | __ http://diveintopython.net/unit_testing/index.html |
| | 320 | __ http://docs.python.org/library/unittest.html |
| | 321 | |
| | 322 | Running your new test |
| | 323 | --------------------- |
| | 324 | |
| | 325 | Remember that we haven't actually made any modifications to |
| | 326 | ``modelform_factory`` yet, so our test is going to fail. Let's run all the |
| | 327 | tests in the ``model_forms_regress`` folder to make sure that's really what |
| | 328 | happens. From the command line, ``cd`` into the Django ``tests/`` directory |
| | 329 | and run: |
| | 330 | |
| | 331 | .. code-block:: bash |
| | 332 | |
| | 333 | ./runtests.py --settings=test_sqlite model_forms_regress |
| | 334 | |
| | 335 | If tests ran correctly, you should see that one of the tests failed with an |
| | 336 | error. Verify that it was the new ``test_factory_with_widget_argument`` test we |
| | 337 | added above, and then go on to the next step. |
| | 338 | |
| | 339 | If all of the tests passed, then you'll want to make sure that you added the |
| | 340 | new test shown above to the appropriate folder and class. It's also possible |
| | 341 | that you have a second copy of Django on your ``PYTHONPATH`` that is taking |
| | 342 | priority over this copy, and therefore it may be that copy of Django whose |
| | 343 | tests are being run. To check if this might be the problem, refer to the |
| | 344 | section `Setting Django up to run the test suite`_. |
| | 345 | |
| | 346 | Writing the code for your ticket |
| | 347 | ================================ |
| | 348 | |
| | 349 | Next we'll be adding the functionality described in `ticket #15315`__ to Django. |
| | 350 | |
| | 351 | Writing the code for ticket #15315 |
| | 352 | ---------------------------------- |
| | 353 | |
| | 354 | Navigate to the ``django/django/forms/`` folder and open the ``models.py`` |
| | 355 | file. Find the ``modelform_factory`` function on line 370 and modify **the |
| | 356 | first part of it** so that it reads as follows: |
| | 357 | |
| | 358 | .. code-block:: python |
| | 359 | |
| | 360 | def modelform_factory(model, form=ModelForm, fields=None, exclude=None, |
| | 361 | formfield_callback=None, widgets=None): |
| | 362 | # Create the inner Meta class. FIXME: ideally, we should be able to |
| | 363 | # construct a ModelForm without creating and passing in a temporary |
| | 364 | # inner class. |
| | 365 | |
| | 366 | # Build up a list of attributes that the Meta object will have. |
| | 367 | attrs = {'model': model} |
| | 368 | if fields is not None: |
| | 369 | attrs['fields'] = fields |
| | 370 | if exclude is not None: |
| | 371 | attrs['exclude'] = exclude |
| | 372 | if widgets is not None: |
| | 373 | attrs['widgets'] = widgets |
| | 374 | |
| | 375 | # If parent form class already has an inner Meta, the Meta we're |
| | 376 | # creating needs to inherit from the parent's inner meta. |
| | 377 | ... |
| | 378 | |
| | 379 | Notice that we're changing one line of code to add a new ``widgets`` keyword |
| | 380 | argument to the function's signature, and then we're adding two lines of code |
| | 381 | a little below that which set ``attrs['widgets']`` to the value of our new |
| | 382 | keyword argument if it was supplied. |
| | 383 | |
| | 384 | Verifying your test now passes |
| | 385 | ------------------------------ |
| | 386 | |
| | 387 | Once you're done modifying ``modelform_factory``, we need to make sure that the |
| | 388 | test we wrote earlier passes now, so we can see whether the code we wrote above |
| | 389 | is working right. To run the tests in the ``model_forms_regress`` folder, |
| | 390 | ``cd`` into the Django ``tests/`` directory and run: |
| | 391 | |
| | 392 | .. code-block:: bash |
| | 393 | |
| | 394 | ./runtests.py --settings=test_sqlite model_forms_regress |
| | 395 | |
| | 396 | If everything passes, then we can move on. If it doesn't, make sure you |
| | 397 | correctly modified the ``modelform_factory`` function as shown above and |
| | 398 | copied the ``test_factory_with_widget_argument`` test correctly. |
| | 399 | |
| | 400 | __ https://code.djangoproject.com/ticket/15315 |
| | 401 | |
| | 402 | Running Django's test suite for the second time |
| | 403 | =============================================== |
| | 404 | |
| | 405 | Once you've verified that your patch and your test are working correctly, it's |
| | 406 | a good idea to run the entire Django test suite just to verify that your change |
| | 407 | hasn't introduced any bugs into other areas of Django. While successfully |
| | 408 | passing the entire test suite doesn't guarantee your code is bug free, it does |
| | 409 | help identify many bugs and regressions that might otherwise go unnoticed. |
| | 410 | |
| | 411 | To run the entire Django test suite, ``cd`` into the Django ``tests/`` |
| | 412 | directory and run: |
| | 413 | |
| | 414 | .. code-block:: bash |
| | 415 | |
| | 416 | ./runtests.py --settings=test_sqlite |
| | 417 | |
| | 418 | As long as you don't see any failures, you're good to go, and you can move on |
| | 419 | to generating a patch file that can be uploaded to Trac. If you do have any |
| | 420 | failures, you'll need to examine them in order to determine whether or not the |
| | 421 | modifications you made caused them. |
| | 422 | |
| | 423 | Generating a patch for your changes |
| | 424 | =================================== |
| | 425 | |
| | 426 | Now it's time to generate a patch file that can be uploaded to Trac or applied |
| | 427 | to another copy of Django. To get a look at the content of your patch, run the |
| | 428 | following command:: |
| | 429 | |
| | 430 | git diff |
| | 431 | |
| | 432 | This will display the differences between your current copy of Django (with |
| | 433 | your changes) and the revision that you initially checked out earlier in the |
| | 434 | tutorial. |
| | 435 | |
| | 436 | Once you're done looking at the patch, hit the ``q`` key to exit back to the |
| | 437 | command line. If the patch's content looked okay, you can run the following |
| | 438 | command to save the patch file to your current working directory:: |
| | 439 | |
| | 440 | git diff > 15315.diff |
| | 441 | |
| | 442 | You should now have a file in the root Django directory called ``15315.diff``. |
| | 443 | This patch file contains all your changes and should look this: |
| | 444 | |
| | 445 | .. code-block:: diff |
| | 446 | |
| | 447 | Index: django/forms/models.py |
| | 448 | =================================================================== |
| | 449 | --- django/forms/models.py (revision 16658) |
| | 450 | +++ django/forms/models.py (working copy) |
| | 451 | @@ -368,7 +368,7 @@ |
| | 452 | __metaclass__ = ModelFormMetaclass |
| | 453 | |
| | 454 | def modelform_factory(model, form=ModelForm, fields=None, exclude=None, |
| | 455 | - formfield_callback=None): |
| | 456 | + formfield_callback=None, widgets=None): |
| | 457 | # Create the inner Meta class. FIXME: ideally, we should be able to |
| | 458 | # construct a ModelForm without creating and passing in a temporary |
| | 459 | # inner class. |
| | 460 | @@ -379,7 +379,9 @@ |
| | 461 | attrs['fields'] = fields |
| | 462 | if exclude is not None: |
| | 463 | attrs['exclude'] = exclude |
| | 464 | - |
| | 465 | + if widgets is not None: |
| | 466 | + attrs['widgets'] = widgets |
| | 467 | + |
| | 468 | # If parent form class already has an inner Meta, the Meta we're |
| | 469 | # creating needs to inherit from the parent's inner meta. |
| | 470 | parent = (object,) |
| | 471 | |
| | 472 | Index: tests/regressiontests/model_forms_regress/tests.py |
| | 473 | =================================================================== |
| | 474 | --- tests/regressiontests/model_forms_regress/tests.py (revision 16658) |
| | 475 | +++ tests/regressiontests/model_forms_regress/tests.py (working copy) |
| | 476 | @@ -263,6 +263,20 @@ |
| | 477 | |
| | 478 | class FormFieldCallbackTests(TestCase): |
| | 479 | |
| | 480 | + def test_factory_with_widget_argument(self): |
| | 481 | + """ Regression for #15315: modelform_factory should accept widgets |
| | 482 | + argument |
| | 483 | + """ |
| | 484 | + widget = forms.Textarea() |
| | 485 | + |
| | 486 | + # Without a widget should not set the widget to textarea |
| | 487 | + Form = modelform_factory(Person) |
| | 488 | + self.assertNotEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) |
| | 489 | + |
| | 490 | + # With a widget should not set the widget to textarea |
| | 491 | + Form = modelform_factory(Person, widgets={'name':widget}) |
| | 492 | + self.assertEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) |
| | 493 | + |
| | 494 | def test_baseform_with_widgets_in_meta(self): |
| | 495 | """Regression for #13095: Using base forms with widgets defined in Meta should not raise errors.""" |
| | 496 | widget = forms.Textarea() |
| | 497 | |
| | 498 | So what do I do next? |
| | 499 | ===================== |
| | 500 | |
| | 501 | Congratulations, you've generated your very first Django patch! Now that you've |
| | 502 | got that under your belt, you can put those skills to good use by helping to |
| | 503 | improve Django's codebase. Generating patches and attaching them to Trac |
| | 504 | tickets is useful, however, you can also :doc:`submit pull requests on Github |
| | 505 | </internals/contributing/writing-code/working-with-git>`. |
| | 506 | |
| | 507 | More information for new contributors |
| | 508 | ------------------------------------- |
| | 509 | |
| | 510 | Before you get too into writing patches for Django, there's a little more |
| | 511 | information on contributing that you should probably take a look at: |
| | 512 | |
| | 513 | * You should make sure to read Django's documentation on |
| | 514 | :doc:`claiming tickets and submitting patches |
| | 515 | </internals/contributing/writing-code/submitting-patches>`. |
| | 516 | It covers Trac etiquette, how to claim tickets for yourself, expected |
| | 517 | coding style for patches, and many other important details. |
| | 518 | * First time contributors should also read Django's :doc:`documentation |
| | 519 | for first time contributors</internals/contributing/new-contributors/>`. |
| | 520 | It has lots of good advice for those of us who are new to helping out |
| | 521 | with Django. |
| | 522 | * After those, if you're still hungry for more information about |
| | 523 | contributing, you can always browse through the rest of |
| | 524 | :doc:`Django's documentation on contributing</internals/contributing/index>`. |
| | 525 | It contains a ton of useful information and should be your first source |
| | 526 | for answering any questions you might have. |
| | 527 | |
| | 528 | Finding your first real ticket |
| | 529 | ------------------------------ |
| | 530 | |
| | 531 | Once you've looked through some of that information, you'll be ready to go out |
| | 532 | and find a ticket of your own to write a patch for. Pay special attention to |
| | 533 | tickets with the "easy pickings" criterion. These tickets are often much |
| | 534 | simpler in nature and are great for first time contributors. Once you're |
| | 535 | familiar with contributing to Django, you can move on to writing patches for |
| | 536 | more difficult and complicated tickets. |
| | 537 | |
| | 538 | If you just want to get started already (and nobody would blame you!), try |
| | 539 | taking a look at the list of `easy tickets that need patches`__ and the |
| | 540 | `easy tickets that have patches which need improvement`__. If you're familiar |
| | 541 | with writing tests, you can also look at the list of |
| | 542 | `easy tickets that need tests`__. Just remember to follow the guidelines about |
| | 543 | claiming tickets that were mentioned in the link to Django's documentation on |
| | 544 | :doc:`claiming tickets and submitting patches |
| | 545 | </internals/contributing/writing-code/submitting-patches>`. |
| | 546 | |
| | 547 | __ https://code.djangoproject.com/query?status=new&status=reopened&has_patch=0&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority |
| | 548 | __ https://code.djangoproject.com/query?status=new&status=reopened&needs_better_patch=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority |
| | 549 | __ https://code.djangoproject.com/query?status=new&status=reopened&needs_tests=1&easy=1&col=id&col=summary&col=status&col=owner&col=type&col=milestone&order=priority |