Best way to fetch and display request parameters in generic CreateView - python

I have a model
class MyModel(models.Model):
slug = models.UUIDField(default=uuid4, blank=True, editable=False)
advertiser = models.ForeignKey(Advertiser)
position = models.SmallIntegerField(choices=POSITION_CHOICES)
share_type = models.CharField(max_length=80)
country = CountryField(countries=MyCountries, default='DE')
# some other Fields. Edited in a ModelForm
This view is called by a url containg position, share_type, country as parameters. I would like to display these parameters in the template. What is the best way to do this. I already have these possibilies
1) use get_context_date and store this in the context
def get_context_data(self, **kwargs):
ctx = super(MyModel, self).get_context_data(**kwargs)
ctx['share_type'] = self.kwargs.get('share_type', None)
ctx['country'] = self.kwargs.get('country', None)
ctx['postal_code'] = self.kwargs.get('postal_code', None)
ctx['position'] = int(self.kwargs.get('position', None))
return ctx
This can then be used in the template
2) use the view variant
def share_type(self):
ret = self.kwargs.get('share_type', None)
return ret
def country(self):
ret = self.kwargs.get('country', None)
return ret
like
<div class="row">
<strong>
<div class="col-sm-3">
Type : {{ view.share_type }}
<div class="col-sm-3">
Country : {{ view.country }}
I think both way are somewhat redundant. Does anybody know a more generic approach to this.
Kind regards
Michael

I think the best way is to do it like this :
def dispatch(self, request, *args, **kwargs):
self.share_type = self.kwargs.get('share_type', None)
self.country = self.kwargs.get('country', None)
self.postal_code = self.kwargs.get('postal_code', None)
self.position = int(self.kwargs.get('position', None))
self.position_verbose = verbose_position(self.position)
ret = super(CreateAdvertisment, self).dispatch(request, *args, **kwargs)
return ret
You can use then in the form_valid method
def form_valid(self, form):
form.instance.advertiser = self.advertiser
form.instance.share_type = self.share_type
form.instance.country = self.country
form.instance.postal_code = self.postal_code
form.instance.position = self.position
ret = super(CreateAdvertisment, self).form_valid(form)
return ret
and in the template of course like this
<strong>
<div class="col-sm-3">
Typ : {{ view.share_type }}
</div>
<div class="col-sm-3">
PLZ : {{ view.postal_code }}
</div>
<div class="col-sm-3">
Ort : TODO
</div>
<div class="col-sm-3">
Pos : {{ view.position_verbose }}
</div>
Many thanks for the proposals.
Regards
Michael

Related

How to handle form with multiple checkboxes with different values and same names in django?

I have a problem with handling a form with multiple checkboxes with different values, but the same names using django.
HTML:
<form name="form1" method="post" id="formToCheck">
{% csrf_token %}
<div class="row form-group">
{% for icon in icons_to_show %}
<div class="col-md-{{ number }}">
<div class="center">
<i class="large material-icons" style="color: darkslategray">{{ icon }}</i>
<p>{{ icon }}</p>
<label for={{ icon }}>
<input type="checkbox" name="display_type" value="{{ icon }}" id={{ icon }}/>
<span>Choose</span>
</label>
</div>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary blue"
style="text-align: center; align-items: center">Submit
</button>
</div>
</form>
models.py
class MemoryResult(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
created_at = models.DateTimeField(auto_now=True, null=True)
picked_imges_names = models.CharField(max_length=255, null=True)
results = models.FloatField(null=True)
status = models.CharField(max_length=255, null=True)
def __str__(self):
return '{}'.format(self.images_names)
#receiver(post_save, sender=User)
def create_memory_results(sender, instance, created, **kwargs):
if created:
MemoryResult.objects.create(user=instance)
views.py
class MemoryImagesView(TemplateView):
template_name = 'accounts/memory.html'
icon_to_show = []
def dispatch(self, request, *args, **kwargs):
previous_images = self.request.user.memoryimage_set.all().last().images_names.strip("[]'").replace("'",
"").replace(
" ", "").split(",")
how_many = 9 - len(previous_images)
self.icon_to_show = render_icons(how_many) + previous_images
response = super(MemoryImagesView, self).dispatch(request, *args, **kwargs)
return response
def get_context_data(self, **kwargs):
context = super(MemoryImagesView, self).get_context_data(**kwargs)
return context
def get(self, request):
memory = MemoryResult.objects.filter(user=request.user)
frontend = {
'title': 'Memory Results',
'number_mem': 'abcdefghi',
'icons_to_show': self.icon_to_show,
'number': 4,
'memory': memory
}
return render(request, 'accounts/memory.html', frontend)
def post(self, request):
picked_icons = []
print(self.icon_to_show)
for i in range(9):
if request._post['display_type'] in self.icon_to_show:
picked_icons.append(request._post['display_type'])
print(picked_icons)
return render(request, 'accounts/home.html')
I know there shouldn't be a for loop, but I was trying different approaches with adding forloop.counter to name of input but it doesn't working as well.
The only value which is saved is the last picked value.
I don't want to use form in forms.py because images changes when refreshaing a page, so I do it in my view.
When I want to read just value from the form with custom name, just the last picked value is saved and I don't know how to get them all.
Can u help me to solve this problem?
To differentiate between elements in HTML, use different ids:
add a counter:
def get(self, request):
memory = MemoryResult.objects.filter(user=request.user)
frontend = {
'title': 'Memory Results',
'number_mem': 'abcdefghi',
'icons_to_show': self.icon_to_show,
'number': 4,
'counter': self.icon_number #initialize it somewhere probably utilizing how_many (I don't know all of your codeú
'memory': memory
}
return render(request, 'accounts/memory.html', frontend)
Then make the id icon + counter

python - django extra_context wont show

I have a form in ListView view using FormMixin that form takes a string lastname
and querys to take a queryset of Account object, I want that Queryset on my template via extra_context.
The form works and I have the queryset that I want, I pass it into self.extra_context and I can see it on console with print BUT it doesn't show in my template. Im feeling stacked, please help me if tou can.
class DepartmentFilesListView(LoginRequiredMixin, SelectRelatedMixin,FormMixin, ListView):
model = FileDetail
login_url = '/'
template_name = 'incomingfiles/departmentFileList_All.html'
context_object_name = 'departmentFiles'
form_class = ChooseOperatorForm
success_url = reverse_lazy('incomingfiles:DepartmentFilesListView')
def __init__(self):
super(DepartmentFilesListView, self).__init__()
now = timezone.now()
self.extra_context = {'title':'Έγγραφα Τμήματος','date':now.date(), 'day':getDay(now.weekday())}
def dispatch(self, request, *args, **kwargs):
return super(DepartmentFilesListView, self).dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
# for
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
lastname = form.cleaned_data['lastname']
lastname = valuateLastname(lastname)
self.extra_context['operators'] = Account.objects.filter(
is_active=True,
department=self.request.user.department,
last_name__contains=lastname
)
print(self.extra_context['operators'])
return super(DepartmentFilesListView, self).form_valid(form)
def form_invalid(self, form):
return super(DepartmentFilesListView, self).form_invalid(form)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['filter_for_all'] = FileDetailFilter(self.request.GET, queryset=self.get_queryset())
num_in_page = 50
paginated_filter_for_all = Paginator(context['filter_for_all'].qs, num_in_page)
filter_for_all_page_number = self.request.GET.get('page')
filter_for_all_page_obj = paginated_filter_for_all.get_page(filter_for_all_page_number)
context['filter_context_obj'] = filter_for_all_page_obj
return context
def get_queryset(self):
self.department = self.request.user.department
return FileDetail.objects.filter(Q(Q(apantitiko__operator__department=self.department)
| Q(departmentassignment__department=self.department)))
And this is the sample of my template
<div class="col-12 Search-operator-div">
<div class="row">
<h2>Αναζήτηση χρεωμένων εγγράφων κατα χειριστή</h2>
</div>
<div class="row" style="width:100%; border-top: 2px solid #212529;"></div>
<br>
<form id="exactΟperator" method="post">
{% csrf_token %}
<div class="row">
{% for field in form %}
<div class="col-1 align-self-center">
<label id="id_{{ field.name }}_label">{{ field.label }}</label>
</div>
<div class="col-3 align-self-center">
{{ field }}
{{ field.error }}
<a role="button" id="cancelOperator" type="button">
<i class="fas fa-times"></i></a>
</div>
{% endfor %}
<div class="col-1 align-self-center">
<input id="SearchOperator" type="submit" value="Αναζήτηση" class="btn btn-primary btn-sm">
</div>
</div>
</form>
<div class="row search-operator-div-content">
{% for operator in operators %}
<div class="col">
<a href="{% url 'incomingfiles:OperatorFilesListView' pk=operator.pk %}" class="link-transition btn-link">
{{ operator }}</a>
</div>
{% endfor %}
</div>
<br>
</div>
In my template I can see anything else is in my self.extra_context but anything Im tried to pass in my form_valid function doesn't shown.
It is like my extra_context cant update in this function.
The default implementation for form_valid() simply redirects to the success_url.
So it returns:
return HttpResponseRedirect(self.get_success_url())
And calling extra_context_data is skipped.
SOLUTION
Return HttpResponse instead super(DepartmentFilesListView, self).form_valid(form):
def form_valid(self, form):
lastname = form.cleaned_data['lastname']
lastname = valuateLastname(lastname)
self.extra_context['operators'] = Account.objects.filter(
is_active=True,
department=self.request.user.department,
last_name__contains=lastname
)
print(self.extra_context['operators'])
return render(self.request, self.template_name, self.get_context_data())
I think this is the easiest method:
class SomeListView(ListView):
.....
extra_context = self.myquery()
.....
def myquery(self):
return { 'query': QuerySet}
Then in template
{{query}}
Well I found a solution but I don't know if that's the correct way to solve that problem.
If you have anything to suggest it will be my pleasure to try it.
To solve my problem I pass the lastname value into the session and query in get_context_data .
def form_valid(self, form):
lastname = form.cleaned_data['lastname']
lastname = valuateLastname(lastname)
if lastname == '':
lastname = '-'
elif lastname == '*':
lastname = ''
self.request.session['lastname'] = lastname
# print(self.extra_context['operators'])
return super(DepartmentFilesListView, self).form_valid(form)
def form_invalid(self, form):
print(self.extra_context['operators'])
return super(DepartmentFilesListView, self).form_invalid(form)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['filter_for_all'] = FileDetailFilter(self.request.GET, queryset=self.get_queryset())
num_in_page = 50
paginated_filter_for_all = Paginator(context['filter_for_all'].qs, num_in_page)
filter_for_all_page_number = self.request.GET.get('page')
filter_for_all_page_obj = paginated_filter_for_all.get_page(filter_for_all_page_number)
context['operators'] = Account.objects.filter(is_active=True,
department=self.request.user.department,
last_name__contains=self.request.session['lastname'])
context['filter_context_obj'] = filter_for_all_page_obj
return context

get_absolute_url not making any effect

I only used this in my template. I am following a tutorial. In tutorial video is working but I couldn't make it work. Here is my template code:
<i> {% for object in object_list %}
<div class="media">
<div class="media-left">
<a href="#">
{% if object.img %} <img class="media-object" src="..." alt="...">
{% endif %}
</a>
</div>
<div class="media-body">
{{ object.content }}<br/>
via {{ object.user }} | {{ object.timestamp|timesince }} ago |
<a href='{{ object.get_absolute_url }}'>View</a>
</div>
</div>
<hr/> </i>
My views.py
<i>
Create your views here.
class TweetCreateView(FormUserNeededMixin, CreateView):
form_class = TweetModelForm
template_name = 'tweets/create_view.html'
class TweetUpdateView(LoginRequiredMixin, UserOwnerMixin, UpdateView):
form_class = TweetModelForm
queryset = Tweet.objects.all()
template_name = 'tweets/update_view.html'
success_url = '/tweets/'
class TweetDeleteView(LoginRequiredMixin, DeleteView):
model = Tweet
template_name = 'tweets/delete_confirm.html'
success_url = reverse_lazy('tweets:home')
class TweetDetailView(DetailView):
template_name = "tweets/detail_view.html"
queryset = Tweet.objects.all()
def get_object(self):
print(self.kwargs)
pk = self.kwargs.get("pk")
print(pk)
return Tweet.objects.get(id=pk)
class TweetListView(ListView):
template_name = "tweets/tweets_list.html"
def get_queryset(self, *args, **kwargs):
qs = Tweet.objects.all()
print(self.request.GET)
query = self.request.GET.get("q", None)
if query is not None:
qs = qs.filter(Q(content=query))
return qs
def get_context_data(self, *args, **kwargs):
context = super(TweetListView, self).get_context_data(*args, **kwargs)
context['create_form'] = TweetModelForm()
context['create_url'] = reverse_lazy('tweets:create')
return context
</i>
my models.py
<i>
class Tweet(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, )
content = models.CharField(max_length=140, validators=[ validate_content ])
updated = models.DateField(auto_now=True, )
timestamp = models.DateField(auto_now_add=True, )
def __str__(self):
return str(self.content)
</i>
You have to add methode get_absolute_url to your models, it's should look like that :
def get_absolute_url(self):
from django.urls import reverse
return reverse('tweets:detail', kwargs={
'pk': self.pk
})

Django Crispy Forms with custom field template is not adding the value of the field to request

I am using Django 1.10 and django-crispy-forms 1.6.1 in my project. I added a custom datetimepicker dropdown template that seems to be functioning correctly. The problem I am now encountering, is that every time I attempt to submit a form, I am flashed with the error on the field that it is required. It seems that after submitting the form, the field is cleared out, and the error class is added to the field.
After submit, form shows errors
When I checked the console, I receive no errors, but when I check Chrome DevTools, I looked at the request payload sent server-side and I see that the field is not added at all to the request. Here is a picture of the request payload from the dev tools.
DevTools request payload without "time_stamp"
I am able to get the datetimepicker to dropdown when the input receives focus, and that seems to work just fine. Here is the relevant code for my forms/views, and the custom template.html:
models.py
class Call(models.Model):
history = AuditlogHistoryField()
analyst = models.ForeignKey(settings.AUTH_USER_MODEL, models.SET_NULL, blank=True, null=True, limit_choices_to={'is_staff': True}, related_name='call_analyst')
contact = models.CharField(max_length=50)
time_stamp = models.DateTimeField(default=datetime.now)
description = models.CharField(max_length=512)
ticket = models.PositiveIntegerField(blank=True, null=True)
CALL_TYPE = (
('M', 'Made'),
('R', 'Received'),
)
call_type = models.CharField(max_length=1, choices=CALL_TYPE)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
#python_2_unicode_compatible
def __str__(self):
return "Call instance {}".format(self.pk)
forms.py
class CallForm(forms.ModelForm):
description = forms.CharField(widget=forms.Textarea)
class Meta:
model = Call
fields = ['analyst', 'contact', 'time_stamp', 'description', 'ticket', 'call_type']
def __init__(self, *args, **kwargs):
super(CallForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_tag = True
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-sm-3'
self.helper.field_class = 'col-sm-9'
self.helper.layout = Layout(
Field('analyst'),
Field('contact'),
Field('time_stamp', template="datetimepicker.html"),
Field('description'),
Field('ticket'),
Field('call_type'),
)
def is_valid(self):
return super(CallForm, self).is_valid()
def full_clean(self):
return super(CallForm, self).full_clean()
def clean_analyst(self):
analyst = self.cleaned_data.get("analyst", None)
return analyst
def clean_contact(self):
contact = self.cleaned_data.get("contact", None)
return contact
def clean_time_stamp(self):
time_stamp = self.cleaned_data.get("time_stamp", None)
return time_stamp
def clean_description(self):
description = self.cleaned_data.get("description", None)
return description
def clean_ticket(self):
ticket = self.cleaned_data.get("ticket", None)
return ticket
def clean_call_type(self):
call_type = self.cleaned_data.get("call_type", None)
return call_type
def clean(self):
return super(CallForm, self).clean()
def validate_unique(self):
return super(CallForm, self).validate_unique()
def save(self, commit=True):
return super(CallForm, self).save(commit)
views.py
class CallCreateView(AjaxCreateView):
model = Call
form_class = CallForm
# fields = ['analyst', 'contact', 'call_timestamp', 'description', 'ticket', 'call_type']
template_name = "reports/../_templates/create_update_template.html"
success_url = reverse_lazy("call_list")
def __init__(self, **kwargs):
super(CallCreateView, self).__init__(**kwargs)
def dispatch(self, request, *args, **kwargs):
return super(CallCreateView, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
return super(CallCreateView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return super(CallCreateView, self).post(request, *args, **kwargs)
def get_form_class(self):
return super(CallCreateView, self).get_form_class()
def get_form(self, form_class=None):
return super(CallCreateView, self).get_form(form_class)
def get_form_kwargs(self, **kwargs):
return super(CallCreateView, self).get_form_kwargs()
def get_initial(self):
return super(CallCreateView, self).get_initial()
def form_invalid(self, form):
return super(CallCreateView, self).form_invalid(form)
def form_valid(self, form):
obj = form.save(commit=False)
obj.save()
return super(CallCreateView, self).form_valid(form)
def get_context_data(self, **kwargs):
ret = super(CallCreateView, self).get_context_data(**kwargs)
return ret
def render_to_response(self, context, **response_kwargs):
return super(CallCreateView, self).render_to_response(context, **response_kwargs)
def get_template_names(self):
return super(CallCreateView, self).get_template_names()
def get_success_url(self):
return reverse("call_detail", args=(self.object.pk,))
datetimepicker.html
{% load crispy_forms_field %}
<div{% if div.css_id %} id="{{ div.css_id }}"{% endif %}
class="form-group{% if form_show_errors and field.errors %} has-error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}{% if div.css_class %} {{ div.css_class }}{% endif %}">
{% if field.label and form_show_labels %}
<label for="{{ field.id_for_label }}"
class="control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label>
{% endif %}
<div class="controls date datetimepicker col-sm-9" id="{{ field.id_for_label }}"
data-link-field="{{ field.id_for_label }}" {{ flat_attrs|safe }}>
<input class="form-control" id="{{ field.id_for_label }}" type="text">
</div>
<input type="hidden" id="{{ field.id_for_label }}" value=""/><br/>
{% include 'bootstrap3/layout/help_text_and_errors.html' %}
</div>
{% block datetimepicker %}
<script type="text/javascript">
$(function () {
$('#{{ field.id_for_label }}').datetimepicker({
sideBySide: true,
allowInputToggle: true,
showTodayButton: true,
showClear: true,
showClose: true,
toolbarPlacement: "top",
format: "dddd, MMMM Do YYYY, h:mm A"
})
})
</script>
{% endblock %}
I think there must be a problem either with my template, or how django-crispy-forms handles fields that use a custom template, because when I remove the template from the form helper, a regular input box is used and the datetime string is submitted just fine to django. Thank you all for your help. Sorry that I had to link the images, I don't have a high enough rep to embed them.
I think the reason why the time_stamp is not being sent to the server because the datepicker input tag doesn't have a name attribute:
<input class="form-control" id="{{ field.id_for_label }}" type="text">
Have a look at the notes in w3schools:
Note: Only form elements with a name attribute will have their values passed when submitting a form.

Django: Adding variable to template context

I have problem with a form if invalid data is submitted. My ErrorView view uses the app/feriehus_detail.html template, but I'm not including price_data in the template context.
This seems to cause a KeyError when the template tries to use price_data as an argument in a filter.
I can't figure out how to add it to the template context? Any help will be much appreciated.
I'm using Python 3.5 and Django 1.9.
Traceback:
Template error:
In template C:\Conference\app\templates\app\feriehus_detail.html, error at line 221
Failed lookup for key [%s] in %r 211 : <td class="text-center">{{ object.price_rec_F }} kr</td>
213 : <td class="text-center">{{ object.price_rec_F|percentage_dif:object.price_cur_F }}</td>
214 : </tr>
215 : </tbody>
216 : </table>
217 : </div>
218 : <div class="col-xs-12" style="padding-bottom:25px;">
219 : <div class="grid">
220 : <div class="col-1-1">
221 : {% column_chart price_data with height='500px' %}
222 : </div>
223 : </div>
224 : </div>
template:
<form role="form" action="{% url 'error' pk=feriehus.id %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit">Send</button>
</form>
forms.py:
class ErrorForm(forms.Form):
content = forms.CharField(
required=True,
widget=forms.Textarea
)
def __init__(self, *args, **kwargs):
super(ErrorForm, self).__init__(*args, **kwargs)
self.fields['content'].label = "Beskriv fejl"
self.helper = FormHelper()
urls.py:
url(r'^feriehus/(?P<pk>[0-9]+)/$', views.FeriehusDetail.as_view(), name='feriehus_detail'),
url(r'^feriehus/(?P<pk>[0-9]+)/error/$', views.ErrorView.as_view(), name='error'),
views.py:
class FeriehusDetail(DetailView, FormMixin):
model = Feriehus
form_class = ErrorForm
def get_context_data(self, **kwargs):
context = super(FeriehusDetail, self).get_context_data(**kwargs)
context['price_data'] = CreateContext.price_time_serie(pk=self.kwargs['pk'])
return context
class ErrorView(FormView):
form_class = ErrorForm
template_name = 'app/feriehus_detail.html'
def get_success_url(self, **kwargs):
return reverse_lazy('feriehus_detail', kwargs={'pk': self.kwargs['pk']})
def get_context_data(self, **kwargs):
context = super(ErrorView, self).get_context_data(**kwargs)
context['object'] = get_object_or_404(Feriehus, pk=self.kwargs['pk'])
context['feriehus'] = get_object_or_404(Feriehus, pk=self.kwargs['pk'])
#context['price_data'] = get_object_or_404(CreateContext.price_time_serie(pk=self.kwargs['pk']))
return context
def form_valid(self, form, **kwargs):
form_content = form.cleaned_data['content']
template = get_template('error_template.txt')
context = Context({
'form_content': form_content
})
content = template.render(context)
email = EmailMessage(
'mail',
content,
'from#email.com' + '',
['to#email.com']
)
email.send()
return super(FeedbackView, self).form_valid(form, **kwargs)
Output of CreateContext.price_time_serie(pk=self.kwargs['pk']):
[{'data': [('Week 49', 654645), ('Week 01', 554645)], 'name': 'Recommended price'}, {'data': [('Week 49', 3398), ('Week 01', 3398)], 'name': 'Current price'}]
As the comment above says, I dont know what CreateContext.price_time_serie() is supposed to do. Until you'll explain that we can only speculate what you are trying to achieve there.
If that is a way how to obtain the primary key for CreateContext record, then you have to add another parameter to the function, as get_object_or_404() expected at least two parameters - the first one is the model class you are trying to get, the others are parameters for the SQL query to identify the record to get. So I guess it should be something like this:
def get_context_data(self, *args, **kwargs):
...
context['price_data'] = get_object_or_404(CreateContext, pk=CreateContext.price_time_serie(pk=self.kwargs['pk']))
...

Categories