{"title":"Codex of Self","link":[{"@attributes":{"href":"https:\/\/selfcodex.com\/","rel":"alternate"}},{"@attributes":{"href":"https:\/\/selfcodex.com\/feeds\/all.atom.xml","rel":"self"}}],"id":"https:\/\/selfcodex.com\/","updated":"2016-01-30T00:00:00-08:00","entry":[{"title":"Using RedirectView in urls.py","link":{"@attributes":{"href":"https:\/\/selfcodex.com\/django-using-redirectview-in-urls-py.html","rel":"alternate"}},"updated":"2016-01-30T00:00:00-08:00","author":{"name":"Ion Scerbatiuc"},"id":"tag:selfcodex.com,2016-01-30:django-using-redirectview-in-urls-py.html","summary":"<p>Django's RedirectView is a generic Class-based View (or CBV for short) that is handy whenever you need to implement redirects. It's especially handy in <code>urls.py<\/code> for simple use cases where you don't need conditional redirects or any other business logic. In this post, I will explore a few use cases where you can use the view directly in the <code>urls.py<\/code> module.<\/p>\n<h2>A simple example<\/h2>\n<p>Suppose that we are revamping some areas of our product and we decide to create the new views from scratch. We want to be able to incrementally ship the new experience, while still keeping the old one around. At the end, we want to switch over to the new experience, without affecting the customers that still use the old URLs (ie. bookmarks, links sent in marketing emails, e.t.c.)<\/p>\n<p>We can easily accomplish that by using <code>RedirectView<\/code>. Here's an example.<\/p>\n<div class=\"highlight\"><pre><span class=\"c\"># urls.py<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">django.conf.urls<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">url<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">django.views.generic.base<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">RedirectView<\/span>\n\n<span class=\"kn\">from<\/span> <span class=\"nn\">myapp.views<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">NewExperienceView<\/span>\n\n<span class=\"n\">urlpatterns<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n    <span class=\"n\">url<\/span><span class=\"p\">(<\/span><span class=\"s\">r&#39;^path\/$&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">RedirectView<\/span><span class=\"o\">.<\/span><span class=\"n\">as_view<\/span><span class=\"p\">(<\/span><span class=\"n\">pattern_name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;new-path&#39;<\/span><span class=\"p\">,<\/span> <span class=\"n\">permanent<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">),<\/span>\n        <span class=\"n\">name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;path&#39;<\/span><span class=\"p\">),<\/span>\n\n    <span class=\"n\">url<\/span><span class=\"p\">(<\/span><span class=\"s\">r&#39;^path\/new\/$&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">NewExperienceView<\/span><span class=\"o\">.<\/span><span class=\"n\">as_view<\/span><span class=\"p\">(),<\/span>\n        <span class=\"n\">name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;new-path&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">]<\/span>\n<\/pre><\/div>\n\n\n<p>It's as easy as that. We use <code>RedirectView.as_view<\/code> to create a new view function that we register with the url resolver. We set the <code>pattern_name<\/code> attribute to instruct it to redirect to that named url.<\/p>\n<p>Note that we also used <code>permanent=True<\/code> to make the redirect permanent (<code>301 Moved Permanently<\/code> instead of <code>302 Found<\/code>). We need this in our case, because this is a permanent switch and at some point we will remove the old URL rules. This is also a <a href=\"https:\/\/support.google.com\/webmasters\/answer\/93633?hl=en\">recomended practice<\/a> by the search engines.<\/p>\n<h2>A slightly more complex example<\/h2>\n<p>What if our old view had required arguments encoded in the URL pattern? Will the approach presented above work?<\/p>\n<p>It will, but only if the new view accepts the same arguments.<\/p>\n<div class=\"highlight\"><pre><span class=\"n\">urlpatterns<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n    <span class=\"n\">url<\/span><span class=\"p\">(<\/span><span class=\"s\">r&#39;^path\/to\/(?P&lt;object_slug&gt;[\\w-]+)\/$&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">RedirectView<\/span><span class=\"o\">.<\/span><span class=\"n\">as_view<\/span><span class=\"p\">(<\/span><span class=\"n\">pattern_name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;new-path&#39;<\/span><span class=\"p\">,<\/span> <span class=\"n\">permanent<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">),<\/span>\n        <span class=\"n\">name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;path&#39;<\/span><span class=\"p\">),<\/span>\n\n    <span class=\"n\">url<\/span><span class=\"p\">(<\/span><span class=\"s\">r&#39;^path\/new\/(?P&lt;object_slug&gt;[\\w-]+)\/$&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">NewExperienceView<\/span><span class=\"o\">.<\/span><span class=\"n\">as_view<\/span><span class=\"p\">(),<\/span>\n        <span class=\"n\">name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;new-path&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">]<\/span>\n<\/pre><\/div>\n\n\n<h2>An even more complex example<\/h2>\n<p>What if the new view doesn't have required arguments? In that case you can't use the above approach, or you'll get a <code>410 Gone<\/code> response.<\/p>\n<p>That's happening because <code>RedirectView<\/code> will try to use <code>reverse<\/code> on the provided <code>pattern_name<\/code> passing the same URL arguments extracted from your URL pattern. If the new view doesn't have arguments, the reverse will fail and you'll get the <code>410 Gone<\/code> response (which is the standard Django response for a URL that is <code>None<\/code>).<\/p>\n<p>How can we fix that? We can set the <code>url<\/code> directly and use <code>reverse_lazy<\/code> to get the actual URL of the new view.<\/p>\n<div class=\"highlight\"><pre><span class=\"c\"># urls.py<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">django.conf.urls<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">url<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">django.core.urlresolvers<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">reverse_lazy<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">django.views.generic.base<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">RedirectView<\/span>\n\n<span class=\"kn\">from<\/span> <span class=\"nn\">myapp.views<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">NewExperienceView<\/span>\n\n<span class=\"n\">urlpatterns<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n    <span class=\"n\">url<\/span><span class=\"p\">(<\/span><span class=\"s\">r&#39;^path\/to\/(?P&lt;object_slug&gt;[\\w-]+)\/$&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">RedirectView<\/span><span class=\"o\">.<\/span><span class=\"n\">as_view<\/span><span class=\"p\">(<\/span><span class=\"n\">url<\/span><span class=\"o\">=<\/span><span class=\"n\">reverse_lazy<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;new-path&#39;<\/span><span class=\"p\">),<\/span> <span class=\"n\">permanent<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">),<\/span>\n        <span class=\"n\">name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;path-to-object-slug&#39;<\/span><span class=\"p\">),<\/span>\n\n    <span class=\"n\">url<\/span><span class=\"p\">(<\/span><span class=\"s\">r&#39;^path\/new\/$&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">NewExperienceView<\/span><span class=\"o\">.<\/span><span class=\"n\">as_view<\/span><span class=\"p\">(),<\/span>\n        <span class=\"n\">name<\/span><span class=\"o\">=<\/span><span class=\"s\">&#39;new-path&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">]<\/span>\n<\/pre><\/div>\n\n\n<p>We used <a href=\"https:\/\/docs.djangoproject.com\/en\/1.9\/ref\/urlresolvers\/#reverse-lazy\">reverse_lazy<\/a> to get the actual URL for the new view and set that as the <code>url<\/code> attribute on the RedirectView. If <code>url<\/code> is set, <code>RedirectView<\/code> will not do a reverse, but instead will try to do a dictionary-style string formatting, passing any arguments that where captured in the URL. In our case, the URL doesn't have any string formatting arguments, so it will remain as it is and the redirect will be handled correctly.<\/p>\n<h2>Other use cases<\/h2>\n<p>For any other use cases you most probably won't be able to use the <code>RedirectView.as_view<\/code> directly. Here are a few examples:<\/p>\n<ul>\n<li>Your new view has different arguments than the old one<\/li>\n<li>You need to conditionally redirect based on the logged-in user or some other rules<\/li>\n<li>You need to perform an operation on redirect (ie. increase an access counter)<\/li>\n<\/ul>\n<p>In these cases you can always subclass <code>RedirectView<\/code> and provide a custom <code>get_redirect_url()<\/code> method. See <a href=\"https:\/\/docs.djangoproject.com\/en\/1.9\/ref\/class-based-views\/base\/#django.views.generic.base.RedirectView\">Django docs on RedirectView<\/a> for more details on how to do that.<\/p>","category":{"@attributes":{"term":"Django"}}},{"title":"Django admin inlines and custom form arguments","link":{"@attributes":{"href":"https:\/\/selfcodex.com\/django-admin-inlines-and-custom-form-arguments.html","rel":"alternate"}},"updated":"2015-11-15T00:00:00-08:00","author":{"name":"Ion Scerbatiuc"},"id":"tag:selfcodex.com,2015-11-15:django-admin-inlines-and-custom-form-arguments.html","summary":"<p>Django admin inlines are a quick and convenient way to embed related objects into a django admin detail page. All is great until you need a custom form to handle the edits for the related objects, that in turn need custom arguments to be passed to the form constructor. In the following post, I'll describe a possible solution to this problem.<\/p>\n<p>You may ask \"Why would I need to pass custom form arguments to a form?\". That's a valid question and most of the time you probably don't. There are cases though when you need additional business logic to happen whenever the value of a field is changed.<\/p>\n<h2>The Problem<\/h2>\n<p>Let's imagine the following use case. We have customers that can have one or more channels associated. Here's a possible way to model these business entities:<\/p>\n<div class=\"highlight\"><pre><span class=\"kn\">from<\/span> <span class=\"nn\">django.db<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">models<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">Customer<\/span><span class=\"p\">(<\/span><span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">Model<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">user<\/span> <span class=\"o\">=<\/span> <span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">ForeignKey<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;auth.User&#39;<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">business_name<\/span> <span class=\"o\">=<\/span> <span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">CharField<\/span><span class=\"p\">(<\/span><span class=\"n\">max_length<\/span><span class=\"o\">=<\/span><span class=\"mi\">100<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">__unicode__<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"nb\">unicode<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">user<\/span><span class=\"p\">)<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">CustomerChannel<\/span><span class=\"p\">(<\/span><span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">Model<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">customer<\/span> <span class=\"o\">=<\/span> <span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">ForeignKey<\/span><span class=\"p\">(<\/span><span class=\"n\">Customer<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">active<\/span> <span class=\"o\">=<\/span> <span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">BooleanField<\/span><span class=\"p\">(<\/span><span class=\"n\">default<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">name<\/span> <span class=\"o\">=<\/span> <span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">CharField<\/span><span class=\"p\">(<\/span><span class=\"n\">max_length<\/span><span class=\"o\">=<\/span><span class=\"mi\">30<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">__unicode__<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">name<\/span>\n<\/pre><\/div>\n\n\n<p>From the admin detail page of the customer you want to be able to deactivate a channel (and maybe later reactivate it). You could come up with the following admin code:<\/p>\n<div class=\"highlight\"><pre><span class=\"kn\">from<\/span> <span class=\"nn\">django.contrib<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">admin<\/span>\n\n<span class=\"kn\">from<\/span> <span class=\"nn\">customers.models<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Customer<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">customers.models<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">CustomerChannel<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">CustomerChannelInline<\/span><span class=\"p\">(<\/span><span class=\"n\">admin<\/span><span class=\"o\">.<\/span><span class=\"n\">TabularInline<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">model<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CustomerChannel<\/span>\n    <span class=\"n\">extra<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">has_delete_permission<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">request<\/span><span class=\"p\">,<\/span> <span class=\"n\">obj<\/span><span class=\"o\">=<\/span><span class=\"bp\">None<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"bp\">False<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">CustomerAdmin<\/span><span class=\"p\">(<\/span><span class=\"n\">admin<\/span><span class=\"o\">.<\/span><span class=\"n\">ModelAdmin<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">inlines<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">CustomerChannelInline<\/span><span class=\"p\">]<\/span>\n    <span class=\"n\">raw_id_fields<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"s\">&#39;user&#39;<\/span><span class=\"p\">,)<\/span>\n\n<span class=\"n\">admin<\/span><span class=\"o\">.<\/span><span class=\"n\">site<\/span><span class=\"o\">.<\/span><span class=\"n\">register<\/span><span class=\"p\">(<\/span><span class=\"n\">Customer<\/span><span class=\"p\">,<\/span> <span class=\"n\">CustomerAdmin<\/span><span class=\"p\">)<\/span>\n<\/pre><\/div>\n\n\n<p>The admin UI should look something like this:<\/p>\n<p><img alt=\"Sample admin detail page with inlines\" src=\"https:\/\/selfcodex.com\/images\/002\/admin_with_inlines.png\" \/><\/p>\n<p>Whenever a staff member needs to deactivate or reactivate one or more channels, they just use the <code>active<\/code> checkboxes in the corresponding inline forms.<\/p>\n<p>But there is a catch. Your company has a policy that requires to have an audit log of all the deactivations\/reactivations: when did the operation happen and who did it. How do we solve it?<\/p>\n<h2>Step 1 - A custom ModelForm<\/h2>\n<p>First of all we need to abstract the operation as a method on the <code>CustomerChannel<\/code> model, so we can invoke it and let it do the necessary business logic. Something like this should work:<\/p>\n<div class=\"highlight\"><pre><span class=\"k\">class<\/span> <span class=\"nc\">CustomerChannel<\/span><span class=\"p\">(<\/span><span class=\"n\">models<\/span><span class=\"o\">.<\/span><span class=\"n\">Model<\/span><span class=\"p\">):<\/span>\n    <span class=\"c\"># ...<\/span>\n\n    <span class=\"nd\">@transaction.atomic<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">change_active<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">operated_by<\/span><span class=\"p\">,<\/span> <span class=\"n\">new_value<\/span><span class=\"p\">):<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">active<\/span> <span class=\"o\">=<\/span> <span class=\"n\">new_value<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">save<\/span><span class=\"p\">()<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">log_active_change<\/span><span class=\"p\">(<\/span><span class=\"n\">operated_by<\/span><span class=\"p\">,<\/span> <span class=\"n\">new_value<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">log_active_change<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">operated_by<\/span><span class=\"p\">,<\/span> <span class=\"n\">activated<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">        Log the deactivation\/reactivation in a separate table that tracks<\/span>\n<span class=\"sd\">        all these operations.<\/span>\n<span class=\"sd\">        &quot;&quot;&quot;<\/span>\n<\/pre><\/div>\n\n\n<p>Next we need a custom ModelForm to handle this. On save, instead of just setting\/unsetting the active flag, we can instead call the <code>change_active<\/code> method on the channel instance.<\/p>\n<p>The form also needs to know what user is responsible for the change, so we need to pass it to the form constructor.<\/p>\n<div class=\"highlight\"><pre><span class=\"kn\">from<\/span> <span class=\"nn\">django<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">forms<\/span>\n\n<span class=\"kn\">from<\/span> <span class=\"nn\">customers.models<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">CustomerChannel<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">CustomerChannelForm<\/span><span class=\"p\">(<\/span><span class=\"n\">forms<\/span><span class=\"o\">.<\/span><span class=\"n\">ModelForm<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">class<\/span> <span class=\"nc\">Meta<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">model<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CustomerChannel<\/span>\n        <span class=\"n\">fields<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"s\">&#39;active&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s\">&#39;name&#39;<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">__init__<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">):<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">staff<\/span> <span class=\"o\">=<\/span> <span class=\"n\">kwargs<\/span><span class=\"o\">.<\/span><span class=\"n\">pop<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;staff&#39;<\/span><span class=\"p\">)<\/span>\n        <span class=\"nb\">super<\/span><span class=\"p\">(<\/span><span class=\"n\">CustomerChannelForm<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">__init__<\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">save<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">commit<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">channel<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">super<\/span><span class=\"p\">(<\/span><span class=\"n\">CustomerChannelForm<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">save<\/span><span class=\"p\">(<\/span><span class=\"n\">commit<\/span><span class=\"o\">=<\/span><span class=\"bp\">False<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"s\">&#39;active&#39;<\/span> <span class=\"ow\">in<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">changed_data<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">channel<\/span><span class=\"o\">.<\/span><span class=\"n\">change_active<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">staff<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">cleaned_data<\/span><span class=\"p\">[<\/span><span class=\"s\">&#39;active&#39;<\/span><span class=\"p\">])<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">channel<\/span><span class=\"o\">.<\/span><span class=\"n\">save<\/span><span class=\"p\">()<\/span>\n<\/pre><\/div>\n\n\n<h2>Step 2 - Use the custom ModelForm with the inline<\/h2>\n<p>Admin inlines allow us to set the form to be used. The problem is that we can't just set the <code>form<\/code> attribute on the inline to <code>CustomerChannelForm<\/code> because the admin will fail to instantiate it, due to the required keyword argument <code>staff=<\/code>.<\/p>\n<p>Unfortunately there is no way to tweak the form instantiation, so our only option seems to be an override for the <a href=\"https:\/\/docs.djangoproject.com\/en\/1.8\/ref\/contrib\/admin\/#django.contrib.admin.InlineModelAdmin.get_formset\"><code>get_formset<\/code> method<\/a> on the inline. This option is not as straightforward as it looks like, because <code>get_formset<\/code> needs to return a FormSet subclass and not a FormSet instance.<\/p>\n<p>Also, currently, there is no way to pass form arguments to a formset class. This was addressed in Django 1.9, which did not reach the final version as of yet. If you're stuck on an older version of Django than 1.9 you can backport the changes from <a href=\"https:\/\/github.com\/django\/django\/pull\/4757\">PR#4757<\/a>.<\/p>\n<p>In our case we can do the following:<\/p>\n<div class=\"highlight\"><pre><span class=\"kn\">from<\/span> <span class=\"nn\">django.utils.functional<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">cached_property<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">django.forms.models<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">BaseInlineFormSet<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">FormArgumentsPassingInlineFormSet<\/span><span class=\"p\">(<\/span><span class=\"n\">BaseInlineFormSet<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">    Backport from Django 1.9 the functionality to pass form kwargs to formsets.<\/span>\n\n<span class=\"sd\">    After upgrading to Django 1.9, you can safely remove this class.<\/span>\n<span class=\"sd\">    &quot;&quot;&quot;<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">__init__<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">):<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">form_kwargs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">kwargs<\/span><span class=\"o\">.<\/span><span class=\"n\">pop<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;form_kwargs&#39;<\/span><span class=\"p\">,<\/span> <span class=\"p\">{})<\/span>\n        <span class=\"nb\">super<\/span><span class=\"p\">(<\/span><span class=\"n\">FormArgumentsPassingInlineFormSet<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">__init__<\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"nd\">@cached_property<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">forms<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">forms<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">_construct_form<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">get_form_kwargs<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">))<\/span>\n                 <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">total_form_count<\/span><span class=\"p\">())]<\/span>\n        <span class=\"k\">return<\/span> <span class=\"n\">forms<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">get_form_kwargs<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">index<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">form_kwargs<\/span><span class=\"o\">.<\/span><span class=\"n\">copy<\/span><span class=\"p\">()<\/span>\n\n    <span class=\"nd\">@property<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">empty_form<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">form<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">form<\/span><span class=\"p\">(<\/span>\n            <span class=\"n\">auto_id<\/span><span class=\"o\">=<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">auto_id<\/span><span class=\"p\">,<\/span>\n            <span class=\"n\">prefix<\/span><span class=\"o\">=<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">add_prefix<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;__prefix__&#39;<\/span><span class=\"p\">),<\/span>\n            <span class=\"n\">empty_permitted<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">,<\/span>\n            <span class=\"o\">**<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">get_form_kwargs<\/span><span class=\"p\">(<\/span><span class=\"bp\">None<\/span><span class=\"p\">)<\/span>\n        <span class=\"p\">)<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">add_fields<\/span><span class=\"p\">(<\/span><span class=\"n\">form<\/span><span class=\"p\">,<\/span> <span class=\"bp\">None<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">return<\/span> <span class=\"n\">form<\/span>\n<\/pre><\/div>\n\n\n<p>Next we need to make the inline to pass the currently logged-in staff in <code>form_kwargs<\/code> keyword argument to the FormSet constructor. The challenge comes from the fact that we need to return a FormSet class from <code>get_formset<\/code>. We also need to make sure all other options that are being passed from the inline admin ar kept (for example <code>fields<\/code> or <code>excludes<\/code>, <code>readonly_fields<\/code>, e.t.c.)<\/p>\n<p>To keep all other inline admin customizations and also override the default <code>BaseInlineFormSet<\/code> we must first call the <code>super()<\/code> implementation and override the <code>formset<\/code> keyword argument.<\/p>\n<p>Next we need to tweak the returned InlineFormSet class to always pass the logged-in staff in the <code>form_kwargs<\/code> keyword argument upon instantiation. That can be done by overriding <code>__new__<\/code> on the FormSet class.<\/p>\n<p>Here is the implementation for the <code>get_formset<\/code> method that accomplishes that.<\/p>\n<div class=\"highlight\"><pre><span class=\"k\">class<\/span> <span class=\"nc\">CustomerChannelInline<\/span><span class=\"p\">(<\/span><span class=\"n\">admin<\/span><span class=\"o\">.<\/span><span class=\"n\">TabularInline<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">model<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CustomerChannel<\/span>\n    <span class=\"n\">form<\/span> <span class=\"o\">=<\/span> <span class=\"n\">CustomerChannelForm<\/span>\n    <span class=\"n\">extra<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">has_delete_permission<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">request<\/span><span class=\"p\">,<\/span> <span class=\"n\">obj<\/span><span class=\"o\">=<\/span><span class=\"bp\">None<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"bp\">False<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">get_formset<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">request<\/span><span class=\"p\">,<\/span> <span class=\"n\">obj<\/span><span class=\"o\">=<\/span><span class=\"bp\">None<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">):<\/span>\n        <span class=\"c\"># Get the default generated InlineFormSet<\/span>\n        <span class=\"n\">kwargs<\/span><span class=\"p\">[<\/span><span class=\"s\">&#39;formset&#39;<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">FormArgumentsPassingInlineFormSet<\/span>\n        <span class=\"n\">InlineFormSet<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">super<\/span><span class=\"p\">(<\/span><span class=\"n\">CustomerChannelInline<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">get_formset<\/span><span class=\"p\">(<\/span><span class=\"n\">request<\/span><span class=\"p\">,<\/span> <span class=\"n\">obj<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">)<\/span>\n\n        <span class=\"c\"># Tweak the formset class so that it will receive the currently<\/span>\n        <span class=\"c\"># logged-in staff on instantiation as a keyword argument for the forms<\/span>\n        <span class=\"k\">class<\/span> <span class=\"nc\">RequestProvidingInlineFormSet<\/span><span class=\"p\">(<\/span><span class=\"n\">InlineFormSet<\/span><span class=\"p\">):<\/span>\n            <span class=\"k\">def<\/span> <span class=\"nf\">__new__<\/span><span class=\"p\">(<\/span><span class=\"n\">cls<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">):<\/span>\n                <span class=\"n\">kwargs<\/span><span class=\"p\">[<\/span><span class=\"s\">&#39;form_kwargs&#39;<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"s\">&#39;staff&#39;<\/span><span class=\"p\">:<\/span> <span class=\"n\">request<\/span><span class=\"o\">.<\/span><span class=\"n\">user<\/span><span class=\"p\">}<\/span>\n                <span class=\"k\">return<\/span> <span class=\"n\">InlineFormSet<\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">)<\/span>\n\n        <span class=\"k\">return<\/span> <span class=\"n\">RequestProvidingInlineFormSet<\/span>\n<\/pre><\/div>\n\n\n<p>That's it! Whenever <code>get_formset<\/code> is called by <code>ConsumerAdmin.get_formsets_with_inlines<\/code>, we dynamically create an InlineFormSet subclass that will tweak the instantiation of the original InlineFormSet to pass the currently logged-in user.<\/p>\n<p>The formset will pass the arguments in <code>form_kwargs<\/code> to all the forms that are being instantiated (which are of the <code>CustomerChannelForm<\/code> type). Finally, the form instances will handle the custom business logic of changing the active flag.<\/p>","category":[{"@attributes":{"term":"Django"}},{"@attributes":{"term":"Django Admin"}}]},{"title":"Django REST Framework upgrade from 2 to 3","link":{"@attributes":{"href":"https:\/\/selfcodex.com\/drf-upgrade-from-2-to-3.html","rel":"alternate"}},"updated":"2015-08-30T00:00:00-07:00","author":{"name":"Ion Scerbatiuc"},"id":"tag:selfcodex.com,2015-08-30:drf-upgrade-from-2-to-3.html","summary":"<p>Upgrading project dependencies always involves risk, especially if they have a lot of backward incompatible changes. About one year ago, <code>Django REST Framework<\/code> (DRF for short) started a big <a href=\"https:\/\/www.kickstarter.com\/projects\/tomchristie\/django-rest-framework-3\">redesign project<\/a> intended to bring improved design, maintainability and debugging capabilities. Unfortunately this redesign implies a lot of backward incompatible changes. This article will present our approach of upgrading DRF and hopefully will help you efficiently plan your's.<\/p>\n<p>A few months ago, I was involved in a big refactoring project at <a href=\"https:\/\/www.rover.com\">Rover.com<\/a>, for which the final goal was to add versioning to our API. The first step was to upgrade DRF to the latest version (which, besides other features, adds support for various versioning schemes). The second step was to implement a particular versioning scheme suitable for our architecture and constraints (more on that in an upcoming post).<\/p>\n<p>In the following sections I will try to present the upgrade path that we took, the main painpoints that we encountered and some general solutions for them. Each use-case and implementation is different, of course, but the following steps should provide you a sense of what the magnitude of the change looks like.<\/p>\n<h3>Prerequisites<\/h3>\n<p>The most important prerequisite for such a big refactoring project is, of course, a <code>comprehensive test suite<\/code>. Our API code is pretty well tested, using both detailed unit tests for the serializers and integration tests for the views. If you lack such a test suite, a project like this is not feasible (I won't talk here about other implications of not having automated tests).<\/p>\n<p>Secondly, you need to <code>read the release announcements<\/code> to understand what have changed and how that will affect your API code. The relevant announcements are located here:<\/p>\n<ul>\n<li><a href=\"http:\/\/www.django-rest-framework.org\/topics\/3.0-announcement\/\">DRF 3.0 announcement<\/a><\/li>\n<li><a href=\"http:\/\/www.django-rest-framework.org\/topics\/3.1-announcement\/\">DRF 3.1 announcement<\/a><\/li>\n<li><a href=\"http:\/\/www.django-rest-framework.org\/topics\/3.2-announcement\/\">DRF 3.2 announcement<\/a><\/li>\n<\/ul>\n<h3>Step 1: Update the Serializers<\/h3>\n<h4><code>request<\/code> is required for serializers with relations<\/h4>\n<p>If you have serializers that define <code>HyperlinkedRelatedField<\/code> or <code>HyperlinkedIdentityField<\/code> relations, you will need to ensure that the serializer receives the request in the context upon initialization. This is almost always true with the views (except for custom made method handlers instantiating the serializers directly). The main painpoint for us was the test suite.<\/p>\n<p>In 2.x series of the framework, a missing request would mean the related fields are serialized to absolute paths (ie. <code>\/api\/v2\/resource\/<\/code>), therefore when writing serializer unit tests you didn't need a request object initialized and passed to the serializer context. That was the case with our serializer tests. This broke badly after upgrade due to the required request in the context.<\/p>\n<p>In order to solve this issue we created a base class (and a mixin) that provides means to create serializers with the request in the context. We then had to update all the tests classes to subclass from the new API TestCase class (or mixin) and remove any hardcoded serializer initialization.<\/p>\n<div class=\"highlight\"><pre><span class=\"k\">class<\/span> <span class=\"nc\">APISerializerTestCaseMixin<\/span><span class=\"p\">(<\/span><span class=\"nb\">object<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">    TestCase mixin for writing API serializer tests.<\/span>\n<span class=\"sd\">    &quot;&quot;&quot;<\/span>\n    <span class=\"n\">serializer_class<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">None<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">get_request<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"n\">APIRequestFactory<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;\/&quot;<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">get_serializer<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">serializer_class<\/span> <span class=\"o\">=<\/span> <span class=\"n\">kwargs<\/span><span class=\"o\">.<\/span><span class=\"n\">pop<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;serializer_class&#39;<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">serializer_class<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">serializer_class<\/span> <span class=\"ow\">is<\/span> <span class=\"bp\">None<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">raise<\/span> <span class=\"ne\">ValueError<\/span><span class=\"p\">(<\/span>\n                <span class=\"s\">&quot;{} requires a `serializer_class`&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"nb\">type<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">__name__<\/span><span class=\"p\">))<\/span>\n\n        <span class=\"n\">context<\/span> <span class=\"o\">=<\/span> <span class=\"n\">kwargs<\/span><span class=\"o\">.<\/span><span class=\"n\">setdefault<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;context&#39;<\/span><span class=\"p\">,<\/span> <span class=\"p\">{})<\/span>\n        <span class=\"n\">context<\/span><span class=\"o\">.<\/span><span class=\"n\">setdefault<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;request&#39;<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">get_request<\/span><span class=\"p\">())<\/span>\n\n        <span class=\"k\">return<\/span> <span class=\"n\">serializer_class<\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">args<\/span><span class=\"p\">,<\/span> <span class=\"o\">**<\/span><span class=\"n\">kwargs<\/span><span class=\"p\">)<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">APISerializerTestCase<\/span><span class=\"p\">(<\/span><span class=\"n\">APISerializerTestCaseMixin<\/span><span class=\"p\">,<\/span> <span class=\"n\">RoverTestCase<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">    Base class for serializer tests that need to extend RoverTestCase.<\/span>\n<span class=\"sd\">    &quot;&quot;&quot;<\/span>\n<\/pre><\/div>\n\n\n<p>With this new set-up, a typical serializer test case will look like:<\/p>\n<div class=\"highlight\"><pre><span class=\"k\">class<\/span> <span class=\"nc\">UserSerializerTests<\/span><span class=\"p\">(<\/span><span class=\"n\">APISerializerTestCase<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">serializer_class<\/span> <span class=\"o\">=<\/span> <span class=\"n\">UserSerializer<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">test_foo<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">serializer<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">get_serializer<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"o\">=<\/span><span class=\"p\">{<\/span><span class=\"s\">&#39;foo&#39;<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">})<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">assertTrue<\/span><span class=\"p\">(<\/span><span class=\"n\">serializer<\/span><span class=\"o\">.<\/span><span class=\"n\">is_valid<\/span><span class=\"p\">())<\/span>\n<\/pre><\/div>\n\n\n<h4>Important changes in <code>DecimalField<\/code><\/h4>\n<p>Starting with 3.0, decimal fields require precision arguments <code>max_digits<\/code> and <code>decimal_places<\/code>. You need to go through\nall your <code>DecimalField<\/code> declarations and update them, or use a custom DecimalField subclass to set sensitive defaults.<\/p>\n<p>One other caveat about DecimalField is that it will enforce the same precision for input data too.\nWe have a few use cases where we use automatically detected geo-localization coordinates (<code>latitude<\/code> and <code>longitude<\/code>)\nand are creating some resources with those values. We configured our location fields to use <code>max_digits=10<\/code> and\n<code>decimal_places=4<\/code>. This is enough precision for all our use cases. The problem was that the values we were getting\nfrom the various geo-localization services could have more than 4 decimal places, and our requests where failing with\nvalidation errors.<\/p>\n<p>In order to fix that we created a custom class <code>PermissiveDecimalField<\/code> and changed the deserialization behavior to\nallow any precision as input, but convert and save it into the provided precision.<\/p>\n<div class=\"highlight\"><pre><span class=\"k\">class<\/span> <span class=\"nc\">PermissiveDecimalField<\/span><span class=\"p\">(<\/span><span class=\"n\">serializers<\/span><span class=\"o\">.<\/span><span class=\"n\">DecimalField<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">    Consider the configured `max_digits` and `decimal_places`, but does not<\/span>\n<span class=\"sd\">    reject input values with different precisions.<\/span>\n<span class=\"sd\">    &quot;&quot;&quot;<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">validate_precision<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">value<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">quantize<\/span><span class=\"p\">(<\/span><span class=\"n\">value<\/span><span class=\"p\">)<\/span>\n<\/pre><\/div>\n\n\n<p>Note that the above implementation works only with 3.1.3 version and above. We submitted a PR to the DRF project\nwith some refactoring for the <code>DecimalField<\/code> internals and the PR was included in the 3.1.3 release. You can find more\ndetails about the initial version of our <code>PermissiveDecimalField<\/code> class on the respective\n<a href=\"https:\/\/github.com\/tomchristie\/django-rest-framework\/pull\/2695\">PR#2695<\/a>.<\/p>\n<h4><code>HyperlinkedRelatedField<\/code> and callable sources<\/h4>\n<p>We had a few serializers where we were using <code>HyperlinkedRelatedField<\/code> with a <code>source=<\/code> argument that was a method on\nthe model instance. That was handy since we could provide a different related object based on some conditions (eg.\nthe authenticated API key or the current state of the object)<\/p>\n<p>Unfortunately this was not working anymore in the 3.x series, because of some internal changes that were meant to\nprovide performance improvements for the related fields. The idea is that for each related field, we usually only need\nthe PK to be included in the output or to construct the hyperlink. Therefore there is no need to do a query to fetch\nthe related object from the database. This was a good idea and it's working great most of the time, except for when\nyou pass a callable as the source.<\/p>\n<p>In order to fix that we had to subclass from <code>HyperlinkedRelatedField<\/code> and override some internals to handle our use\ncases well. The good news is that we submitted a PR to fix this behavior and it was merged into the 3.2.0 release. More\ndetails on the issue can be found on the respective <a href=\"https:\/\/github.com\/tomchristie\/django-rest-framework\/pull\/2690\">PR#2690<\/a><\/p>\n<h4>Other notable changes<\/h4>\n<h4><code>allow_null<\/code> and <code>allow_blank<\/code><\/h4>\n<p>In previous versions of DRF, the <code>required<\/code> argument to serializer fields meant that the field could be missing from\nthe input, but could also be included with a value of <code>null<\/code> or empty string. This changed in the 3.x series, and if\nyour code is relying on this behavior (our was), you'll have to do some cleanup to make it work.<\/p>\n<p>Use <code>required=False<\/code> when the field can be missing from the input. This is handy for example when you are adding a new\nfield to a serializer, but don't want to break existing clients. The old clients will not send the field and it will\nbe ignored; the new ones will send it and you'll be able to consume it.<\/p>\n<p>Use <code>allow_null<\/code> if you want to allow a <code>null<\/code> as a valid value for a particular field. This is handy when you have\ndata fields or behavior that have a special meaning when they are <code>None<\/code>.<\/p>\n<p>Use <code>allow_blank<\/code> for any <code>CharField<\/code> and subclasses to signal that you allow an empty string <code>''<\/code> as a valid value for\nthe field.<\/p>\n<p>You can find more information about these arguments <a href=\"http:\/\/www.django-rest-framework.org\/topics\/3.0-announcement\/#serializer-fields\">here<\/a>.<\/p>\n<h4><code>field_from_native<\/code> and <code>field_to_native<\/code> were removed<\/h4>\n<p>We had a few instances where we where relying on these two fields to perform some special data transformations for the\nserialization\/deserialization steps. Since these fields were removed in 3.0, we had to rework our logic around the new\n<code>to_internal_value<\/code> and <code>to_representation<\/code> methods, or in some cases we had to create custom field classes to handle\nour custom logic.<\/p>\n<h4>Nested serializer errors<\/h4>\n<p>DRF 3.x changed the way errors for nested serializers are returned. Previously, the errors were returned as a dict\ninside a list, which was a little bit weird. DRF 3.x fixed that weirdness by returning a dict directly:<\/p>\n<div class=\"highlight\"><pre><span class=\"c\"># DRF 2.x example<\/span>\n<span class=\"n\">errors<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n    <span class=\"s\">&#39;request&#39;<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\n        <span class=\"p\">{<\/span>\n            <span class=\"s\">&#39;request_version&#39;<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span><span class=\"s\">&#39;Invalid request version&#39;<\/span><span class=\"p\">]<\/span>\n        <span class=\"p\">}<\/span>\n    <span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c\"># DRF 3.x example<\/span>\n<span class=\"n\">errors<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n    <span class=\"s\">&#39;request&#39;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n        <span class=\"s\">&#39;request_version&#39;<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span><span class=\"s\">&#39;Invalid request version&#39;<\/span><span class=\"p\">]<\/span>\n    <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/pre><\/div>\n\n\n<p>The fix is nice of course, but out consumer apps were relying on this structure so we had to keep backwards\ncompatibility until we can get a new version of the API out. In order to overcome this issue, we overrode the <code>errors<\/code>\nproperty in the parent serializer and made sure it wraps the errors of nested serializers in lists.<\/p>\n<h4>Serialization of <code>Date<\/code>\/<code>Time<\/code>\/<code>DateTime<\/code> and <code>Decimals<\/code><\/h4>\n<p>The serialization behavior of <code>Date<\/code>\/<code>Time<\/code>\/<code>DateTime<\/code> and <code>Decimals<\/code> changed in DRF 3.x so that the values of these\nfields are coerced to strings in <code>serializer.data<\/code>. All our serializer tests were relying on the fact that they are\nreturned as objects when doing the assertions. We had to either update all of our serializer tests to meet the new\nrequirements, or to update the respective settings to keep the old behavior. Because of the time pressure we chose the\nsecond option.<\/p>\n<p>More info about these changes <a href=\"http:\/\/www.django-rest-framework.org\/topics\/3.0-announcement\/#date-and-time-objects-as-iso-8859-1-strings-in-serializer-data\">here<\/a> and\n<a href=\"http:\/\/www.django-rest-framework.org\/topics\/3.0-announcement\/#decimals-as-strings-in-serializer-data\">here<\/a>.<\/p>\n<h4>Other issues that we encountered<\/h4>\n<ul>\n<li>HyperlinkedRelatedField was failing to reverse non-API views. Fixed in 3.1.2, <a href=\"https:\/\/github.com\/tomchristie\/django-rest-framework\/pull\/2724\">PR#2724<\/a><\/li>\n<li>Couldn't use nested serializers with <code>many=True<\/code> and <code>allow_null=True<\/code>. Fixed in 3.2, <a href=\"https:\/\/github.com\/tomchristie\/django-rest-framework\/pull\/2766\">PR#2766<\/a><\/li>\n<\/ul>\n<h3>Step 2: Update the Views<\/h3>\n<p>After the serializer problems are fixed (which for us accounted for almost 90% of the issues), we need to tackle the\nviews.<\/p>\n<h4>No more <code>PaginationSerializer<\/code><\/h4>\n<p>DRF 3.1 introduced a new <a href=\"http:\/\/www.django-rest-framework.org\/topics\/3.1-announcement\/#pagination\">pagination API<\/a> and,\nin the process, removed the old way of customizing the paginated results.<\/p>\n<p>We could no longer subclass <code>PaginationSerializer<\/code> and add more data to the serialized output. In order to do that we\nhad to subclass <code>PageNumberPagination<\/code> instead and override <code>get_paginated_response<\/code>.<\/p>\n<p>The following two example yield the same serialized output.<\/p>\n<div class=\"highlight\"><pre><span class=\"c\"># DRF 2.x example<\/span>\n<span class=\"k\">class<\/span> <span class=\"nc\">MyPaginationSerializer<\/span><span class=\"p\">(<\/span><span class=\"n\">pagination<\/span><span class=\"o\">.<\/span><span class=\"n\">PaginationSerializer<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">    Add a new field to the paginated output.<\/span>\n<span class=\"sd\">    &quot;&quot;&quot;<\/span>\n    <span class=\"n\">extra_field<\/span> <span class=\"o\">=<\/span> <span class=\"n\">serializers<\/span><span class=\"o\">.<\/span><span class=\"n\">SerializerMethodField<\/span><span class=\"p\">(<\/span><span class=\"s\">&#39;get_extra_field&#39;<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">get_extra_field<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">        Return the extra field payload<\/span>\n<span class=\"sd\">        &quot;&quot;&quot;<\/span>\n\n<span class=\"k\">class<\/span> <span class=\"nc\">MyView<\/span><span class=\"p\">(<\/span><span class=\"n\">ListAPIView<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">pagination_serializer_class<\/span> <span class=\"o\">=<\/span> <span class=\"n\">MyPaginationSerializer<\/span>\n\n<span class=\"c\"># DRF 3.x example<\/span>\n<span class=\"k\">class<\/span> <span class=\"nc\">MyPagination<\/span><span class=\"p\">(<\/span><span class=\"n\">pagination<\/span><span class=\"o\">.<\/span><span class=\"n\">PageNumberPagination<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">    Add a new field to the paginated output.<\/span>\n<span class=\"sd\">    &quot;&quot;&quot;<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">get_paginated_response<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">data<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">response<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">super<\/span><span class=\"p\">(<\/span><span class=\"n\">MyPagination<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">get_paginated_response<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">response<\/span><span class=\"p\">[<\/span><span class=\"s\">&#39;extra_field&#39;<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">get_extra_field<\/span><span class=\"p\">()<\/span>\n        <span class=\"k\">return<\/span> <span class=\"n\">response<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">get_extra_field<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;<\/span>\n<span class=\"sd\">        Return the extra field payload<\/span>\n<span class=\"sd\">        &quot;&quot;&quot;<\/span>\n\n<span class=\"k\">class<\/span> <span class=\"nc\">MyView<\/span><span class=\"p\">(<\/span><span class=\"n\">ListAPIView<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">pagination_class<\/span> <span class=\"o\">=<\/span> <span class=\"n\">MyPagination<\/span>\n<\/pre><\/div>\n\n\n<h3>Step 3: Celebrate<\/h3>\n<p>When all the tests are green, ship the changes and celebrate the adoption of the new and awesome DRF 3.x framework.<\/p>","category":[{"@attributes":{"term":"Django"}},{"@attributes":{"term":"Django-REST-framework"}},{"@attributes":{"term":"API"}},{"@attributes":{"term":"REST"}}]}]}