forked from twitter/opensource-website
-
Notifications
You must be signed in to change notification settings - Fork 1
/
generic-layouts-in-crispy-forms.html
186 lines (157 loc) · 10.5 KB
/
generic-layouts-in-crispy-forms.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<title>Generic Layouts in Crispy Forms [ brack3t ]</title>
<meta name="description" content="">
<meta name="author" content="Brack3t, aka Kenneth Love and Chris Jones">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/ico" href="./brack3t-theme/assets/favicon.ico">
<link href="./feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="brack3t ATOM Feed">
<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Le styles -->
<link href="http://fonts.googleapis.com/css?family=Exo:200,300,500,700,900,200italic,300italic,500italic,700italic,900italic" rel="stylesheet">
<link href="./brack3t-theme/assets/bootstrap/css/bootstrap.css" rel="stylesheet">
<link href="./brack3t-theme/assets/github.css" rel="stylesheet">
<link href="./brack3t-theme/assets/bootstrap/css/brack3t.css" rel="stylesheet">
<script>
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "UA-4642386-4"]);
_gaq.push(["_trackPageview"]);
(function() {
var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script type="text/javascript">
var disqus_identifier = "generic-layouts-in-crispy-forms.html";
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://brack3t.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
</head>
<body>
<div class="container">
<div class="row-fluid">
<div class="span8">
<header id="logo" role="banner">
<h1><a href="/">Brack3t</a></h1>
<p>Two guys… and Python.</p>
</header>
</div>
<aside class="span2" id="sidebar" role="complementary">
<nav>
<ul class="unstyled">
<li><a href="./pages/projects.html">Projects</a></li>
<li><a href="./archives.html">Archives</a></li>
<li><a href="./tags.html">Tags</a></li>
</ul>
</nav>
</aside>
</div>
<div class="row-fluid">
<div class="span7 offset1" id="main" role="main">
<article>
<header>
<h1><a href="./generic-layouts-in-crispy-forms.html" class="slabtext">Generic Layouts in Crispy Forms</a></h1>
<h6><span class="permalink">Published: <a href="./generic-layouts-in-crispy-forms.html">06-26-2012</a></span>
<span class="author">by <strong>Kenneth</strong></span>
<span class="tags">tags: <a href="./tag/django.html">django</a> <a href="./tag/django-crispy-forms.html">django-crispy-forms</a> <a href="./tag/tip.html">tip</a> </span>
</h6>
</header>
<p>Just a quick tip and sanity check, today, about something I ran into with <a class="reference external" href="https://github.com/maraujop/django-crispy-forms">django-crispy-forms</a>, the awesome new form library from Miguel Araujo.</p>
<p>This morning, I converted the project we've been building for a client (currently some 1,700 or so files, counting templates, CSS, and icons) from <tt class="docutils literal"><span class="pre">django-uni-form</span></tt> to <tt class="docutils literal"><span class="pre">django-crispy-forms</span></tt>. It's a pretty painless
transition, actually. Just do some find-and-replace across your files, basically changing any instance of <tt class="docutils literal">uni-</tt> to <tt class="docutils literal">crispy-</tt> (well, and <tt class="docutils literal">form</tt> to <tt class="docutils literal">forms</tt>), and you're good to go. Then, however, I wanted to
convert two large forms that we have, which share 90% of their fields, to using the sharable <tt class="docutils literal">Layout</tt> objects that <tt class="docutils literal"><span class="pre">django-crispy-forms</span></tt> gives us.</p>
<p>Basically, the forms looked like this:</p>
<div class="highlight"><pre><span class="x">class FirstForm(GenericAppFormForTheExample):</span>
<span class="x"> ...</span>
<span class="x"> def __init__(self, *args, **kwargs):</span>
<span class="x"> ...</span>
<span class="x"> self.helper = FormHelper()</span>
<span class="x"> self.helper.layout = Layout(</span>
<span class="x"> "field1",</span>
<span class="x"> "field2",</span>
<span class="x"> "special-field",</span>
<span class="x"> [field3 through field20]</span>
<span class="x">class SecondForm(GenericAppFormForTheExample):</span>
<span class="x"> ...</span>
<span class="x"> def __init__(self, *args, **kwargs):</span>
<span class="x"> ...</span>
<span class="x"> self.helper = FormHelper()</span>
<span class="x"> self.helper.layout = Layout(</span>
<span class="x"> "field1",</span>
<span class="x"> "field2",</span>
<span class="x"> "special-field2",</span>
<span class="x"> [field3 thorugh field20]</span>
</pre></div>
<p>Obviously that was a lot of repetition that we could cut out now that these inheritable layouts exist. By the way, I'm pretty sure this would have been possible in <tt class="docutils literal"><span class="pre">django-uni-form</span></tt> but likely not as friendly.</p>
<p>First I started off by creating the shared resources. I made two of them since our special fields come in the middle of our layouts.</p>
<div class="highlight"><pre><span class="n">form_intro_layout</span> <span class="o">=</span> <span class="n">Layout</span><span class="p">(</span>
<span class="s">"field1"</span><span class="p">,</span>
<span class="s">"field2"</span>
<span class="p">)</span>
<span class="n">form_common_layout</span> <span class="o">=</span> <span class="n">Layout</span><span class="p">(</span>
<span class="p">[</span><span class="n">field3</span> <span class="n">through</span> <span class="n">field20</span><span class="p">]</span>
<span class="p">)</span>
</pre></div>
<p>And I added them each into my forms. I'm only going to show one form but you'll get the idea.</p>
<div class="highlight"><pre><span class="x">self.form_intro_layout = form_intro_layout</span>
<span class="x">self.form_intro_layout.insert(-1, "special-field")</span>
<span class="x">self.helper.layout = Layout(</span>
<span class="x"> self.form_intro_layout,</span>
<span class="x"> form_common_layout</span>
<span class="x">)</span>
</pre></div>
<p>And, display-wise, it worked fine. This is, ideally, exactly what you do to extend these layouts. <strong>But</strong>, this causes problems in testing.</p>
<p>We test all of our views, models, and forms. The form tests passed fine, but some of the view tests were throwing up warnings about fields being referenced more than once and, for example, <tt class="docutils literal"><span class="pre">special-field2</span></tt> missing
from <tt class="docutils literal">FirstForm</tt>. Obviously something was up with the inheritance and with how I was instantiating objects.</p>
<p>My next step was to stop using <tt class="docutils literal">self</tt> on the instance-specific versions of the generic layouts and just give them a local name like <tt class="docutils literal">standard_intro_layout</tt> (the <tt class="docutils literal">standard</tt> name makes more sense internally and its
meaning isn't important for this example), but that didn't stop the errors. It did, however, still work fine visually.</p>
<p>I tried using a <tt class="docutils literal">.copy()</tt> method on the layout, but that doesn't exist. So I turned to the <tt class="docutils literal">copy</tt> module of Python, specifically the <tt class="docutils literal">deepcopy</tt> method. I tried with just the standard <tt class="docutils literal">copy</tt> method, but it had
the same effect of working visually, but throwing warnings on tests.</p>
<p>The new version looked like this:</p>
<div class="highlight"><pre><span class="x">from copy import deepcopy</span>
<span class="x">standard_intro_layout = deepcopy(form_intro_layout)</span>
<span class="x">standard_intro_layout.insert(-1, "special-field")</span>
<span class="x">...</span>
</pre></div>
<p>That works perfectly! It still appears the same visually, like all the methods have, and it also quiets the tests so they pass without warnings.</p>
<p>Unless someone knows of a reason I shouldn't use <tt class="docutils literal">deepcopy</tt>, this seems to be the way to solve this problem. I'm also not sure if it was the <tt class="docutils literal">self</tt> (which I doubt, since I removed it and the warnings still
happened), or the fact that both forms extend the same abstract form, or some other variable that led to the problem. Regardless, I'm happy to have solved it.</p>
</article>
<section>
<header>
<h1>Comments</h1>
</header>
<div id="disqus_thread"></div>
</section>
</div>
</div>
<footer><p>© Brack3t. All rights reserved. <a href="./feeds/all.atom.xml">ATOM feed</a></p></footer>
</div> <!-- /container -->
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="./brack3t-theme/assets/jquery-1.8.2.min.js"></script>
<script src="./brack3t-theme/assets/modernizr.js"></script>
<script src="./brack3t-theme/assets/jquery.slabtext.min.js"></script>
<script src="./brack3t-theme/assets/jquery.fittext.js"></script>
<script src="./brack3t-theme/assets/highlight.pack.js"></script>
<script>
$(function() {
$(".slabtext").slabText({
"maxFontSize": 200,
"viewportBreakpoint": 768
});
$(".highlight pre").each(function(i, e) {hljs.highlightBlock(e, " ")});
});
</script>
</body>
</html>