I'm quiet new to Django, and I've been struggling with the following:
I have a view that initially has set1=0(False) and set2=1(True). A user can swap this,so to set set1=1(True) and set2=0(False). The user can do this by pushing the 'Update' button. My problem is, that it does change in the backend, but the frontend/view/html is not updated. And I have no idea why..
I created a small example, which, if this works, I'll also get my Django case working..
I have the following:
views.py
So first this view is used, and the initial values for set1 and set2 are set:
class MainSettingsView(generic.TemplateView):
extra_context = {"main_page": "active"}
context_object_name = 'main'
template_name = 'index.html'
set1 = int(model.set1 == True)
set2 = int(model.set2 == True)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
model = Setting12.objects.get(pk=1)
context['set1'] = int(model.set1 == True)
context['set2'] = int(model.set2 == True)
return context
And then, based on the chosen option in the radio buttons, it changes the model/db and therefore also the set1 and set2:
class UpdateButton(generic.TemplateView):
extra_context = {"main_page": "active"}
context_object_name = 'setting12'
template_name = 'index.html'
def post(self, request, queryset=None, *args, **kwargs):
if update_type == "set1":
model = Setting12.objects.get(pk=1)
model.set1 = True
model.set2 = False
return redirect(reverse("main"))
elif update_type == "set2":
model = Setting12.objects.get(pk=1)
model.set1 = False
model.set2 = True
return redirect(reverse("main"))
For context, this is what I use in the html:
index.html
<form class="card" action="{% url 'setting12' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<h3 class="card-title">Update Settings</h3>
<input type="radio" name="update_type" value="set1" {% if view.set1 %}checked {%endif%}> set1
{% if view.set1 is 1 %} ( Current setting ) {% else %} {% endif %}</input>
<input type="radio" name="update_type" value="set2" {% if view.set2 %}checked {%endif%}> Set2
{% if view.set2 is 1 %} ( Current setting ) {% else %} {% endif %}</input>
<button class="btn btn-primary" type="submit">Update</button>
</form>
I do see that the db is changed, set1 and set2 are changed in value when the button is pushed. But I also see is that when it goes from MainSettingsView > UpdateButton > MainSettingsView that the model is not re-read.. it doesn't go over the model again. So I do think that that is the problem of not getting a updated view. But I can't find a way to do it...
Can someone help me? It's taking me days so far :s
I finally got it =D!!
By removing the view. part in my index.html, and removing
set1 = int(model.set1 == True)
set2 = int(model.set2 == True) I removed the whole initial setting that I couldn't overwrite anymore. After that it totally worked :)
Related
It's been days I am trying to split up a simple django form with some charfield and dropdowns to sub forms using django-formtools. In the first step form I left 2 dropdowns field and in the second step form, 2 char fields and one image file. Once I change dropdowns and go to the second form then after step back, the values and image of the second step disappear so I need to refill the second step of form before submitting(and values of dropdown in the first step saved in the storage after changing the step):
Here is the view.py code:
FORMS = [('step_first', step_first_form),
('step_second', step_second_form)]
TEMPLATES = {'step_first': 'myapp/step_first.html',
'step_second': 'myapp/step_second.html'}
class NewWizard(NamedUrlSessionWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'photos'))
def done(self, form_list, **kwargs):
.....
def get_form(self, step=None, data=None, files=None):
# maintaining the files in session when changing steps
if self.steps.current == 'step_first':
step_files = self.storage.get_step_files(self.steps.current)
else:
step_files = self.storage.current_step_files
if step_files and files:
for key, value in step_files.items():
if files in key and files[key] is not None:
step_files[key] = files[key]
elif files:
step_files = files
return super(NewWizard, self).get_form(step, data,step_files)
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
and my second template:
<form id="dropdownForm" method="POST" action="" enctype="multipart/form-data">{% csrf_token %}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
<div class="content-section mt-4 text-center">
<div class="row justify-content-center">
{% if wizard.steps.prev %}
<button class="btn btn-sm btn-outline-primary" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}" formnovalidate>{% trans "prev step" %}</button>
{% endif %}
<div class="form-group ml-2">
<input class="btn btn-sm btn-outline-primary" type="submit" value="{% trans 'submit' %}"/>
</div>
</div>
</div>
{% endif %}
in url.py:
create_wizard = login_required(NewWizard.as_view(FORMS, url_name='step_first', done_step_name='finished'))
urlpatterns = [
re_path('myapp/new/create-(?P<step>.+)', create_wizard, name='step_first'),
]
I guess the prev step does not submit the form!
Any assistance you can provide would be greatly appreciated.
The default behavior of form wizard is that if you go back and come back to the current form, you will lose all the data(non-file) and files. The reason is that the prev button is associated with render_goto_step method. In the doc about render_goto_step, it says:
This method is called when the step should be changed to something else than the next step. By default, this method just stores the requested step goto_step in the storage and then renders the new step. If you want to store the entered data of the current step before rendering the next step, you can overwrite this method.
The following will solve part of your problems.
class NewWizard(NamedUrlSessionWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'photos'))
def done(self, form_list, form_dict, **kwargs):
return render(self.request, 'simpletest/done.html', {
'form_data':[form.cleaned_data for form in form_list]
})
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def render_goto_step(self, goto_step, **kwargs):
print('under render_goto_step')
print(self.storage.current_step)
form1 = self.get_form(self.storage.current_step, data=self.request.POST,files=self.request.FILES)
if form1.is_valid:
print("form.is valid")
self.storage.set_step_data(self.storage.current_step, self.process_step(form1))
print('after set_step_data')
self.storage.set_step_files(self.storage.current_step, self.process_step_files(form1))
print('after set_step_files')
else:
print('under form.errors')
print(form1.errors)
######### this is from render_goto_step method
self.storage.current_step = goto_step
form = self.get_form(
data=self.storage.get_step_data(self.steps.current),
files=self.storage.get_step_files(self.steps.current))
return redirect(self.get_step_url(goto_step))
Unfortunately, it cannot solve the image preview problem. I am not entirely sure about it, but this seems not related to the render_goto_step function per se because even the ones saved by postmethod to the session storage cannot be rendered. For example, if you add an image in form2, hit submit, and go to form3, and hit prev, you will see that image in form2 is gone, although the value(title) is there.
It seems that django and form wizard just do not render these files because they are dictionaries not files themselves. They are either <UploadedFile> or <InMemoryUploadedFile>objects.
What to do about image preview?
1. I was able to solve this problem by saving the file data into Model. Override post method and render_goto_step method to make sure that the image is saved to model both when you hit submit (post) and hitprev, first--- render_goto_step.
In addition, in order to render the image in your template, override get_context_data method and pass mypostinstance.
Please note that: in the following code, I simplified the save to modelportion by just save to pk=1 object. You have to change it accordingly.
class NewWizard(NamedUrlSessionWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'photos'))
def done(self, form_list, form_dict, **kwargs):
return render(self.request, 'simpletest/done.html', {
'form_data':[form.cleaned_data for form in form_list]
})
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def render_goto_step(self, goto_step, **kwargs):
print('under render_goto_step')
print(self.storage.current_step)
form1 = self.get_form(self.storage.current_step, data=self.request.POST,files=self.request.FILES)
if form1.is_valid:
print("form.is valid")
self.storage.set_step_data(self.storage.current_step, self.process_step(form1))
print('after set_step_data')
self.storage.set_step_files(self.storage.current_step, self.process_step_files(form1))
print('after set_step_files')
############ check if it is step_second, save file to model.
if self.steps.current =='step_second':
print('under render_goto_step step_second')
if 'imagefile' in self.request.FILES.keys():
f = self.request.FILES['imagefile']
print(f)
if f:
mypost = MyPost.objects.get(pk=1)
mypost.image = f
mypost.save()
print('saved')
else:
print('under form.errors')
print(form1.errors)
######### this is from render_goto_step method
self.storage.current_step = goto_step
form = self.get_form(
data=self.storage.get_step_data(self.steps.current),
files=self.storage.get_step_files(self.steps.current))
return redirect(self.get_step_url(goto_step))
def post(self, *args, **kwargs):
wizard_goto_step = self.request.POST.get('wizard_goto_step', None)
if wizard_goto_step and wizard_goto_step in self.get_form_list():
return self.render_goto_step(wizard_goto_step)
print('wizard_goto_step')
print(wizard_goto_step)
print('current')
print(self.steps.current)
# get the form for the current step
form = self.get_form(data=self.request.POST, files=self.request.FILES)
# and try to validate
if form.is_valid():
self.storage.set_step_data(self.steps.current, self.process_step(form))
self.storage.set_step_files(self.steps.current, self.process_step_files(form))
############ check if it is step_second, save file to model.
if self.steps.current =='step_second':
print('under step_second')
f = self.request.FILES['imagefile']
print(f)
if f:
mypost = MyPost.objects.get(pk=1)
mypost.image = f
mypost.save()
print('saved')
return self.render_next_step(form)
else:
# check if the current step is the last step
if self.steps.current == self.steps.last:
# no more steps, render done view
return self.render_done(form, **kwargs)
else:
# proceed to the next step
return self.render_next_step(form)
return self.render(form)
return super(NewWizard, self).post(*args, **kwargs)
def get_context_data(self, form, **kwargs):
context = super().get_context_data(form=form, **kwargs)
mypost = MyPost.objects.get(pk=1)
context.update({'mypost': mypost})
return context
and in your template, use image.url like below:
<div class="preview">%
{% if mypost.image %}
<img id="fileip-preview" src="{{mypost.image.url}}" alt="....">
{% else %}
<img id="fileip-preview" src="" alt="....">
{% endif %}
</div>
2. Another viable way is to first read the file and pass it to the template in views. I did not try to implement this. But this post might provide you an idea about how to implement it.
3. Another seemingly viable way is to use history API and localStorage. The idea is to mimic the browser back and forward buttons. As you can see when you use the browser to go back and come back to current, you can see that all your info is retained. It seemed that so many things that users do can affect history states, such as using back/forward rather than prev, submit; refreshing pages, back/forward from refreshed pages, etc. I tried this approach and felt like it should be a winner, but abandoned it eventually.
I am creating a multi-choice quiz app, I have created a view which shows the question and 4 option. I have given radio button to each option but is giving me this error:
MultiValueDictKeyError at /quiz/2/11/ 'choice'
views.py
def question_detail(request,question_id,quiz_id):
q = Quiz.objects.get(pk=quiz_id)
que = Question.objects.get(pk=question_id)
ans = que.answer_set.all()
selected_choice = que.answer_set.get(pk=request.POST['choice'])
if selected_choice is True:
come = que.rank
came = come + 1
later_question = q.question_set.get(rank=came)
return render(request,'app/question_detail.html',{'que':que , 'later_question':later_question, 'ans':ans})
else:
come = que.rank
later_question = q.question_set.get(rank=come)
return render(request, 'app/question_detail.html', {'que': que, 'later_question': later_question, 'ans': ans})
question_detail.html
<form action="{% 'app:detail' quiz_id=quiz.id question_id=que.id %}" method="post">
{% csrf_token %}
{% for choice in que.answer_set.all %}
<input type="radio" name="choice" id="choice{{forloop.counter}}" value="{{choice.id}}">
<label for="choice{{forloop.counter}}">{{choice.answer}}</label>
{% endfor %}
</form>
Okay like I said in my comment, you're most likely getting that error because the POST object will be empty during a normal GET request. So you should wrap everything that's meant to happen after a request in an IF block:
if request.method === 'POST':
selected_choice = que.answer_set.get(pk=request.POST['choice'])
# Every other post-submit task
You should always check for the POST method in your views if you're expecting form data. Others have answered this more in-depth before so I would just direct you there:
What does request.method == "POST" mean in Django?
I'm working on my first django project which is a sport betting app. My models are:
class Game(models.Model):
home_team = models.CharField(max_length=100)
away_team = models.CharField(max_length=100)
class GameBet(models.Model):
gameid = models.ForeignKey(Game)
bet = models.IntegerField(default=None) #values: 1(home win), 0 (draw), 2 (away win)
userid = models.ForeignKey(User)
I am able to save user bets using a single game view, when I pass gameid in url, but that is not very effective.
I created a simple page with list of games with 1 form per game for a start (at the end I would prefer to send all bets with one button):
{% for game in games %}
{{ game.id }} | {{ game.home_team }} | {{ game.away_team }} | <form method="post"> {% csrf_token %} {{ form }} <input type="submit" value="bet" /> </form> <br> {% endfor %}
and my form is:
if request.method == 'POST':
#if request.POST["bet"] == 'bet':
form = NewBetForm(request.POST)
if form.is_valid():
bet = form.save(commit=False)
bet.gameid = [r.id for r in owing_games] #it gives me a list and ValueError Cannot assign "[10, 11, 12]": "GameBet.gameid" must be a "Game" instance.
bet.userid_id = current_user.id
bet.bet = form.value()
bet.save()
How can I pass a single game.id in that case?
EDIT:
I think I can use request.POST.get(something) but I don't know how to pass my {{ game.id }} from template to views
Create a hidden input field with value as game.id.
Example:
<input type='hidden' value='{{ game.id }}' name='game_id'>
Place the above html code within the form block. Now, you can access the value in the view as request.POST['game_id'].
And, if you want to place same bet for multiple game ids, then loop over game ids, retrieve the Game instance from database and assign each new GameBet instance gameid as the retrieved Game instance.
Single Game ID Example:
game_id = request.POST['game_id']
if request.method == 'POST':
# rest of the code
if form.is_valid():
bet.game_id = Game.objects.get(id=game_id)
Multiple Game IDs Example:
game_ids = request.POST['game_ids']
if request.method == 'POST':
for game_id in game_ids:
bet = form.save(commit=False)
bet.game_id = Game.objects.get(id=game_id)
bet.save()
# return response after loop
I am struggling with a requirement now, I want to add an image to choice field label and I really dont have a clue how to do it. I am using django form wizard to render the form.
Here is the image which shows what I want to achieve :
And here is what I have got right now ( to make the radio buttons inline, I know it could be achieved through css):
Here is the forms.py:
from django import forms
from django.utils.translation import gettext as _
CHOICES=[('0','Pay by card'), ('1','Invoice')]
class PaymentForm(forms.Form):
title = 'payment'
payment_method = forms.ChoiceField(label = _("Payment Options"), choices=CHOICES, widget=forms.RadioSelect(), required = True)
I am rendering using wizard form:
{{ wizard.form.payment_method.label_tag }}
{{ wizard.form.payment_method|safe }}
{{ wizard.form.payment.errors}}
Anyone has any suggestion for this apart from custom widget?
Without a widget:
from django.utils.safestring import mark_safe
CHOICES=[('0', mark_safe('Pay by card <img src="by_card.jpg"/>')),
('1', mark_safe('Invoice <img src="no_card.jpg"/>'))
]
Credit: setting help_text for each choice in a RadioSelect
1) Shortly (but i'm not sure) call a function where 'Pay by card' and return all <img>... that you need.
2) You can make somthing like #Gahbu said
3)Long [Better, i think, but untested :( ]:
Make a renderer:
from myapp.my_widgets import CardsRadioFieldRenderer
CARD_CHOICE = '0'
CHOICES=[(CARD_CHOICE,'Pay by card'), ('1','Invoice')]
class PaymentForm(forms.Form):
title = 'payment'
payment_method = forms.ChoiceField(label = _("Payment Options"), choices=CHOICES,widget=forms.RadioSelect(renderer=CardsRadioFieldRenderer), required = True)
# myapp/my_widgets.py
class CardRadioInput(RadioInput):
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
choice_value = force_text(choice[0])
self.choice_value = choice_value
if choice_value == CARD_CHOICE:
choice_label = force_text(self.get_html_for_card_choice())
else:
choice_label = force_text(choice[1])
self.choice_label = choice_label
self.index = index
def get_html_for_card_choice(self):
#some logic to get the images tags (<img ...> <img ...>)
return text
class CardsRadioFieldRenderer(RadioFieldRenderer):
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return CardRadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
In your template do something like :
{% for choice in wizard.form.payment_method.choices %}
{{ choice.0 }} {# value #} {{ choice.1 }} {# value #}
{% if choice.0 == PAYMENT_BY_PAYPAL %}
...
{% endif %}
{% endfor %}
You can also write :
{% for key, value in wizard.form.payment_method.choices %}
{% if key == PAYMENT_BY_PAYPAL %}
...
{% endif %}
{% endfor %}
If you want to tweak it from the template, you need to use the "safe" which tells Jinja not to escape it. like below
{{ some_object.some_property|safe }}
Yet if you want to use it inside your python code while defining the form/modelform just use mark_safe as below.
mark_up = 'this is a link'
choices = list()
choices.append(('some_id', mark_safe(mark_up)))
self.fields['some_field'].choices = choices
Argh. Hey all, i have a muy simple django question:
And argh, i'm sorry, i've read and read, and I am sure this is covered somewhere super-obviously, but i haven't found it!
How do i edit/update a model using django? Like, the data values? Not the model fields?
Here is my code! (I'm using a home-brew version of stuff!)
Here is the urls:
url(r'^editStory/(?P<id>\d+)$',
StoryModelView.as_view(
context_object_name='form',
template_name ='stories/editStory.html',
success_template= 'stories/editStorySuccess.html'
),
{},
'editStory'
),
Here is the view:
def get(self,request,id=None):
form = self.getForm(request,id)
return self.renderValidations(form)
def getForm(self, request,id):
if id:
return self.getModelById(request,id)
return StoryForm()
def getModelById(self,request,id):
theStory = get_object_or_404(Story, pk=id)
if theStory.user != request.user:
raise HttpResponseForbidden()
return StoryForm(theStory)
def renderValidations(self,form):
if self.context_object_name:
contextName = self.context_object_name
else:
contextName = 'form'
if self.template_name:
return render_to_response(self.template_name,{contextName:form})
else :
return render_to_response('stories/addStory.html',{contextName:form})
def getPostForm(self,request,id):
if id:
theStory = self.idHelper(request,id)
return StoryForm(request.POST,theStory)
return StoryForm(request.POST)
def processForm(self,form,request):
theStory = form.save(commit=False)
theStory.user = request.user
return theStory
Here is the template code:
{% block content %}
<h3>Edit story</h3>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
<input type="submit" value="Submit"/>
</form>
{% endblock %}
try as i might, i either:
get an error
get nothing
i get an error with the code as-displayed, caused by this line
{% for field in form %}
and with the error of:
Caught AttributeError while rendering: 'Story' object has no attribute 'get'
or i get nothing - no data at all - if i change my "getModelById" method to read:
def getModelById(self,request,id):
theStory = get_object_or_404(StoryForm, pk=id)
if theStory.user != request.user:
raise HttpResponseForbidden()
return theStory
StoryForm is one of those "ModelForm" things, and its model is "Story".
SO! The question:
how do i fix this code to make it work? What have i done wrong?
You don't show what your class is inheriting from, but it seems like you're just using a standard single object display view. Instead, you should use one of the editing mixins that are provided for this purpose.
Without knowing what your model looks like, are you looking for something along the lines of
s = Story.objects.get(some criteria)
s.user = <some user>
s.save()
?
Sorry, I find your question a little vague.
Upon rereading, one thing jumped out at me:
You can't do a query (get, filter, or any variation on these) on a model-- you have to do it on an object manager, like objects.
So, as above, in your case, Story.objects.get_object_or_404 should solve your error.