Django - ModelForms rendered indexed by foreignKey and that value POSTed - django

A simple and logical extension of the tutorial polls app would be showing several questions per page with a single 'submit' button. I've got a hybrid design that uses ModelForms for multi-field questions but individually rendered fields for single-field questions. So the standard question_set page is rendered by:
{% load custom_tags %}
{% block question_set %}
<form action="{% url 'polls:answer' user.username %}" method="POST">
{% csrf_token %}
{% for question in page_question_list %}
<hr/>
{% if question.answer_type == "CH" %}
{% include "polls/Choice_Answer_form.html" %} {%endif%}
{% if question.answer_type == "SA" %}
{% include "polls/Short_Answer_form.html" %} {%endif%}
{% if question.answer_type == "LA" %}
{% include "polls/Long_Answer_form.html" %} {%endif%}
{% if question.answer_type == "E3" %}
{% include "polls/EJ_Answer_form.html" with form=forms|get_item:question%}
{%endif%}
{% if question.answer_type == "E4" %}
{% include "polls/EJ_Answer_form.html" with form=forms|get_item:question%}
{%endif%}
{% if question.answer_type == "BS" %}
{% include "polls/brainstorm_form.html" %}
{% endif %}
{% endfor %}
<br/>
<hr/>
<input type="submit" value="Submit" />
</form>
{% endblock %}
I have a sense that I have missed something fundamental about how forms are supposed to work. When you create a ModelForm object and send it out to be rendered, it knows what instance (in my case, question number and user_id) it corresponds to in the model. When it comes back from the browser, that information is gone if you have disabled, read-only'd or hidden those (id, pk etc) fields. You could put that information in the url, but it will only be feasible if you are dealing with a single row of data on each page. In the docs are [examples][1] cheerfully showing how to ModelForm(request=request, instance=instance) but I don't know how to design it so that the request and the instance stay in sync.
Some strategies I've looked into:
bundle needed forms in formsets and use the 'prefix' to differentiate them. Not sure that the question number association can be retained for rendering (suspect it can) or recovered if it is still dropped from the POST data (suspect it can't).
modifying the rendered field-names to include the Key information (question number, user) to prevent them overlapping in html namespace and only the last values being submitted e.g. generate a new field for the form with the data in it to be rendered and presumably passed back or define a function to set a html_field_name in the constructor.
use the form 'auto_id' (see docs) string to give every field a unique name that encodes question number - suspect this ought to be redundant and better handled by the pros in a formset, but see my first idea.
I probably want to try the easiest and best first; any advice gratefully received.
(I had a bunch more links but am not allowed to post them with so little status)

I found that loading and retrieving the question number from the 'prefix' (but not auto_id) field as a string "qNN#" for each form can work (because the prefix is applied to the element name and auto_id applies to the id, and its the name that the POST data uses). I've put all this logic in 'views.py'
The view that catches the submitted form has to look at the keys in request.POST and when it finds a key beginning with q, it works out what question the fields come from and passes the request off with the question to create the form from the request + prefix.
This means that request.POST is sent to my form_instantiator helper function each time there is form_data to be found, rather than submitting them as a group to be iterated over.
I feel guilty of cruelty to Django, somehow.

Related

django-widget-tweaks using field.name in render_field

I am using django-widget-tweaks and its render_field. I have an odd situation and I wonder if there is a workaround or a different approach that I should use?
I am using django and allauth (I am not using the social auth part yet). I have the default django user model and I have a "Profile" model that extends the user model. That's all working.
This is a specialized "Membership App". Only adults over 18 should be able to login. Once logged in a person can create "member" one or more "member" records. The logged in person is responsible for payng for memberships but might not be a member themselves; for example, a parent might log in and create member records for youth under 18 while not being a member themselves.
When creating a new member record I need to give the logged in person the opportunity to create a member record for themselves (if they don't already have one). If they chose this option then the logged in person's "Profile" info should auto-fill the new member form. To facilitate this I am capturing and serializing the logged in user's Profile info and am passing it to the CBV MemberCreate method. This method renders the Member Create form using django-widget-tweaks and its render_field method, a la:
{% with field=newMemberForm.first_name %}
{% render_field field class="form-control form-control-sm" %}
{% endwith %}
I want to create a data-loggedindata= attribute that uses the field.name value as the "member name" of the logged in person's profile. SOmething like:
{% with field=newMemberForm.first_name %}
{% render_field field class="form-control form-control-sm" data-loggedindata=LoggedInProfile.{{field.name}} %}
{% endwith %}
But django-widget-tweaks doesn't seem to understand that and I am not wise enough in the world of django to know a workaround.
I can hear someone typing: "but you know the field name from the {% with ... %} construct". Yes, that is true but the Member form has a lot of fields so I wrapped the render_field part in a small html file that I include. So the reall segment above might look like:
{% with field=profileForm.first_name %}
<div class="form-group col col-3">
{% include "Members/template_includes/field_render.html" %}
</div>
{% endwith %}
And the included file has the render_field in it. Inside that included html file I have no idea of the name of the field.
ideas?
Edit
I have tried Pankaj's suggestion below but
{% render_field field|attr:"data-loggedindata=LoggedInProfile.{{field.name}}" %}
does not appear to interpolate the object LoggedInProfile or the {{field.name}} value. What I get is the un-interpolated string "LoggedInProfile.{{field.name}}".
I have tried:
{% render_field field|attr:"data-loggedindata:getattr(LoggedInProfile, field.name" %}
but that is not interpolated either. Is there a way in a Django template and/or widget-tweaks to programatically get the value of an object attribute?
Edit 2
I decided to go another way. I am still using widget-tweaks but I am not trying to use render_field in this way anymore. I am serializing the data that would be placed in the data- elements and placing the JSON version in a hidden form field. I am letting javascript deal with it now.
If you want to add attribute in field with django-widget-tweaks you can simply use attr for that, in your case it would be like -
{% with field=newMemberForm.first_name %}
{% render_field field|add_class:"form-control form-control-sm"|attr:"data-loggedindata=LoggedInProfile.{{field.name}}" %}
{% endwith %}
Still have any doubts let me know

How can I create a custom template for a TabularInline admin Formset in Django?

In my Django app (research database), when changing a person object in the admin, I'd like all of the sources for that person to be listed as hyperlinks to the file for that source. I'm trying to do this by creating a custom template for a stacked inline. Here is the custom template so far:
<p>Testing</p>
{% for form in inline_admin_formset %}
{% for fieldset in form %}
<h5>Fieldset</h5>
{% if fieldset.name %} <h2>{{ fieldset.name }}</h2>{% endif %}
{% for line in fieldset %}
<h6>Line</h6>
{% for field in line %}
<h6>Field</h6>
{{ field.field }}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
A lot of this is just for me to see what's going on. I used the links here and here as sort of a guide. What renders from the {{ field.field }} is what you'd expect from an inline element - a dropdown menu with the source names as choices and some icons for adding/changing.
What I really want, however, is just the source name rendered as a hyperlink. How do I get the source name (the actual name of the attribute is source_name) from what I have in the Django template language (i.e. the "field" object)?
In that context, {{ field.field }} is a BoundField object, and the value method is probably what you would want to use, as is in {{ field.field.value }}.
A more Django-ish approach (and more complicated) might involve creating a custom widget (start by subclassing one of their built-ins) that only displays text, and then hook that into the form being used in the ModelAdmin for your model. I think there's a bit of a rabbit hole there, in terms of needing to subclass the BaseInlineFormset and possibly a few others down that chain... I'm seeing that the BaseFormSet class has a .form attribute referenced in its construct_form method, but things are little murky from there.
Might also be useful to check out this past thread: Override a form in Django admin

Control the form errors display while using {{ form.as_ul }} in Django templates.

I like the convenient output form method {{ form.as_ul }} but is there a way I can still continue to use it but capture all the errors upfront instead of displaying the error just above each field.
I understand that there are ways to loop through each form element and so on as mentioned in django docs but I want to continue to utilize the capability of form.as_ul() except get control over error display.
Solved this problem by using Reusable Form Templates.
Its simple...
Create a reusable template with the following code snippets based on your need.
If you want to display all errors right at the top of the form...
{% if form %}
{% if form.errors %}
{% for field in form %}
{{field.errors}}
{% endfor %}
{% endif %}
{% for field in form %}
<li>{{ field.label_tag }}: {{ field }}</li>
{% endfor %}
{% endif %}
If you want to display all errors right after each form field without the default html elements around error use...
{% for field in form %}
{{ field.label_tag }}: {{ field }}
{% for error in field.errors %}{{ error }}{% endfor %}
{% endfor %}
Used the second template and created a Inclusion Tag
The only way I see is to inherit the new form class from forms.Form and alter as_ul method as you like. Which isn't very good if you are going to use third-party forms like login form and so on (they won't have this method).
I think the best solution is to create your own inclusion tag and render form with it. It will be as short as as_ul ({% render_form form %}) but very flexible, it will work with all forms and won't mix HTML and Python code.
I still think the customization for rendering form in templates is quite flexible. I usually do this for my webapps. You may work with a bit javascript and css but not a big problem. Moreover, I think we should try to make the app simple.

Django, Pulling a Value from a Model based on an ID

I'm relatively new to Django and as a test of my knowledge I'm trying to set up a project where it displays a list of games between anchor tags, when one of the game tags is clicked on, it pulls information about that game from a model using the ID (primary key) relative to that name. For instance the first games ID would be 1 and so on.
However, I am uncertain as to how to approach building a view for this. The only way I was able to get information from a template before was from user input (input tag) and then using request.GET to take the information from the input.
So far in this project, anchor tags are linking to a different URL which has the view which gets the information based on the id, then it should refresh the page and the information should display. Everything should be easy, but I'm just having trouble thinking of a way to get the id of the game based on which link is clicked. Is there a way I can simply set the value of this ID somewhere and reference it in the view, or rather pull the id of the game based on which link is clicked?
Code:
{% extends "base.html" %} <!-- Extends the base models html design-->
{% block title %}Arcade{% endblock %}
{% block content %}
{% if games %}
<p>Found {{ games|length }} game{{ games|pluralize }}</p>
{% for game in games %}
<li>{{ game.game_name }}</li><!--Game Link->
{% endfor %}
{% else %}
<p>There are currently no games in the database.</p>
{% endif %}
{% if results %}
{% endif %}
{% endblock %}
I hope I did an adequate job explaining this problem.
To simplify it further: How do I pull any kind of value from a template?
Thank You
Keith
This is simple. In each iteration of your for loop, you have an object called game, which is presumably a model instance. So you can just do:
<li>{{ game.game_name }}</li>
Or, even better, use the reverse URL functionality:
<li>{{ game.game_name }}</li>
You should look at using AJAX for this.
When the user clicks a tag, have the page asynchronously GET or POST to your django backend. If you want to GET, write a url to the effect of
^/data/game/(\d+)
then grab the id, get your model instance, and return some json or xml.

django blog - post- reply system display replies

I have a mini blog app, and a reply system. I want to list all mini blog entries, and their replies, if there are any.
i have in views.py
def profile_view(request, id):
u = UserProfile.objects.get(pk=id)
paginator = New.objects.filter(created_by = request.user)
replies = Reply.objects.filter(reply_to = paginator)
return render_to_response('profile/publicProfile.html', {
'object_list': u,
'list':paginator,
'replies':replies
},
context_instance=RequestContext(request))
and in the template:
<h3>Recent Entries:</h3>
{% for object in list %}
<li>{{ object.post }} <br />
{% for object in replies %}
{{ object.reply }} <br />
{% endfor %}
mention : reply_to is a ForeignKey to New, and New is the name of the 'mini blog' table
But it only shows all the replies for each blog entry, not the reply for every entry, if there is one
thanks
Firstly, please consider giving your variables sensible names. New, list and paginator do not at all describe what those objects actually are - and in some cases they are actively misleading, as with object_list which is not a list at all but a single item.
Secondly, the code is doing exactly what you asked it to (ignoring the fact that the template code you have posted is actually invalid, because you are not nesting the loops properly and redefine the object variable). You get all entries, and all replies, then show the list of all replies underneath every entry.
Instead, you obviously want to get the replies attached to each entry. So delete the lines referring to Reply in the view, and in the template make use of the reverse relationship:
{% for object in list %}
{{ object.post }}
{% for reply in object.reply_set.all %}
{{ reply }}
{% endfor %}
{% endfor %}
Finally, as others have recommended in answer to your other questions, you would be well served by reading the Django tutorial and the Django book, both available freely online.