Friday, July 25, 2014

The pain of programming: trying to get this little bit of data from here to there

So many things have been driving me crazy about my web development project that I got completely overwhelmed with trying to document it all.  So I'm picking one little thing to share, as representational of all of the barriers.  It's this:

I am trying to add Song Opinions to my "Songs not in 4/4" website.  I can now import the hundreds of Songs and Song Opinions that people submitted to the old, and I have the user interface for Songs working, if rudimentary.  So I'm really just adding Song Opinions to the user interface.  And in fact, I previously got it to where you can glimpse the Song Opinions when you look at the Songs.  On this screen:

"Action This Day" and "Queen" and "Hot Space" come from the Song table, and "2/4 (according to nic)" comes from the Song Opinions table.  Now I want to make "Add an Opinion" work.  I am relying on the time-saving conveniences of Django (which is why I picked it to try out as a web application framework), and indeed, I can get this whole form below to appear with only one or two lines of code *:

Where I'm presently stuck is this: When you click Done, the website needs to keep track of what Song you are submitting an Opinion for.  So I need to stuff a hidden field into this form that contains the number, 1259, which happens to be the arbitrary ID number in my database for Action This Day.  And figuring out the code to pass that number from the Add an Opinion link only took ten minutes (I had to force myself not to go dig up a debate I'd vaguely tracked about the implicit grammar of POST and GET and what was properly RESTful (c.f. this for just a taste.)  Be Agile: just get something working, and then perfect it later).  That code, by the way, is

<p><a href="{% url 'songs:song_opinion_create' %}">Add an Opinion</a></p>
Where I'm currently stuck is, okay, I can get that number from point A (the Song detail page) to point B (hovering somewhere in the aether, or more specifically, in the request context for the Song Opinions Create/Update form, whatever that means), but I can't get it to Point C, into the form on the actual web page for Song Opinions Create/Update, which is where it needs to be to get to Point D, whatever doohickey processes the form and saves the Song Opinion, so that the doohickey knows that I am talking about Action This Day.  I can see it in Point B, thanks to the lovely debug interface in Django:

But exactly what magic incantation of code will nudge it forward escapes me, even though I thought I had figured it out previously.  Some things that are not it, for your amusement:
{{ song_id }}
{{ context.song_id }}
{{ context:song_id }}
{{ request.song_id }}
{{ request:song_id }}
{{ request['song_id'] }}
{{ 'song_id' }}
{{ ['song_id'] }}
{{ please give me my song id }}
{{ I am begging you }}
{{ song_id }}
{{ I know I already tried that but I was hoping maybe you would give me a pass this time }}
Total time spent just on attempting and failing to add Song Opinions (including time spent on an ill-fated foray into poorly documented Generic Class-Based View Formsets, and time spent using Mercurial to easily sweep away the debris of that ill-fated foray, and time spent manually cleaning up the debris of using Mercurial to automatically and easily sweep away the debris of the ill-fated foray, and writing this blog post): over 3 hours and counting.

* If Song Opinion is already defined in the database, then the code necessary to get an almost working creation form for Song Opinions is:

class SongOpinionCreate(CreateView):
    model = SongOpinion
    form_class = SongOpinionForm

class SongOpinionForm(forms.ModelForm):
    class Meta:
        model = SongOpinion

and also
<form action="" method="post">
{% csrf_token %}
    {{ form.as_p }}
    <input name="song_id" type="hidden" value="{{ song_id}}" />
    <input type="submit" value="Done" />

and I guess also this:

     url(r'^opinion/create/(?P<song_id>\d+)/$', SongOpinionCreate.as_view(), name='song_opinion_create'),
Which altogether isn't too bad, although I do wonder why I need to specify the model in two different places, which is odd given that DRY (Don't Repeat Yourself) is one of the many guiding principles of the cool programmers these days.

UPDATE: I couldn't help myself; I had to google a bit more and finally fix this little problem. A solution, maybe not the best, who knows, is to add more code, because even though Django clearly knows what song_id is in the aether (as evidenced by its existence in the debug toolbar), before I can pull that information down in my template (point C in my model), I have to push it from one place in the aether to another, using this code:
    def get_context_data(self, **kwargs):
        context = super(SongOpinionCreate, self).get_context_data(**kwargs)
        context["song_id"] = self.kwargs.get('song_id')
        return context
Simple and elegant, right?  Just a matter of kwargs.

No comments :

Post a Comment