Translations tabs of Django parler not showing in the view - django

I'm using django parler to translate my models. now i'm creating a custom admin Panel and i have a view for create and update of Contents. I'm using a class based view inherit from "View" for both create and update views so i can't use the TranslatableCreateView and TranslatableUpdateView. I saw in the codes of Django parler that using TranslatableModelFormMixin you can add translation support to the class-based views. I used this mixin but still i don't have access to the language tabs.
Here is Views.py:
class ContentCreateUpdateView(TranslatableModelFormMixin, TemplateResponseMixin, View):
module = None
model = None
obj = None
template_name = 'content-form.html'
def get_model(self, model_name):
if model_name in ['text', 'video', 'image', 'file']:
return apps.get_model(app_label='courses', model_name=model_name)
return None
def get_form(self, model, *args, **kwargs):
Form = modelform_factory(model, exclude=['owner',
'order',
'created',
'updated'])
return Form(*args, **kwargs)
def dispatch(self, request, module_id, model_name, id=None):
self.module = get_object_or_404(Module, id=module_id, course__owner=request.user)
self.model = self.get_model(model_name)
if id:
self.obj = get_object_or_404(self.model,
id=id,
owner=request.user)
return super().dispatch(request, module_id, model_name, id)
def get(self, request, module_id, model_name, id=None):
form = self.get_form(self.model, instance=self.obj,)
return self.render_to_response({'form': form, 'object': self.obj})
def post(self, request, module_id, model_name, id=None):
form = self.get_form(self.model, instance=self.obj, data=request.POST, files=request.FILES)
if form.is_valid():
obj = form.save(commit=False)
obj.owner = request.user
obj.save()
if not id:
# new content
Content.objects.create(module=self.module, item=obj)
return redirect('module_content_list', self.module.id)
return self.render_to_response({'form': form, 'object': self.obj})
Template:
{% extends '_base.html' %}
{% load crispy_forms_tags %}
{% load crispy_forms_filters %}
{% block content %}
<div class="col-md-12">
<!-- Horizontal Form -->
<div class="card card-primary">
<div class="card-header ">
{% if object %}
<h3 class="card-title mb-0 float-left"> Edit Content "{{ object.title }}"</h3>
{% else %}
<h3 class="card-title mb-0 float-left"> Add New Content</h3>
{% endif %}
</div>
<!-- /.card-header -->
<!-- form start -->
<div class="card-body">
<form method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-12">
{% if language_tabs %}
<ul class="nav nav-tabs">
{% for url,name,code,status in language_tabs %}
{% if status == 'current' %}
<input type="hidden" class="language_button selected" name="{{ code }}"/>
<li class="nav-item">
<a class="current nav-link active"
aria-selected="true">{{ name }}</a>
</li>
{% else %}
<li class="nav-item">
<a class="{{ status }} nav-link"
href="{{ url }}"
aria-selected="false">{{ name }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</div>
<div class="col-6">
{{ form }}
</div>
</div>
{% csrf_token %}
<div class="col-6">
<button class="btn btn-success">Save Content</button>
</div>
</form>
</div>
</div>
</div>
<!-- /.card -->
{% endblock %}
Here is the source code of django parler:
class TranslatableModelFormMixin(LanguageChoiceMixin):
"""
Mixin to add translation support to class based views.
For example, adding translation support to django-oscar::
from oscar.apps.dashboard.catalogue import views as oscar_views
from parler.views import TranslatableModelFormMixin
class ProductCreateUpdateView(TranslatableModelFormMixin, oscar_views.ProductCreateUpdateView):
pass
"""
def get_form_class(self):
"""
Return a ``TranslatableModelForm`` by default if no form_class is set.
"""
super_method = super().get_form_class
# no "__func__" on the class level function in python 3
default_method = getattr(
ModelFormMixin.get_form_class, "__func__", ModelFormMixin.get_form_class
)
if not (super_method.__func__ is default_method):
# Don't get in your way, if you've overwritten stuff.
return super_method()
else:
# Same logic as ModelFormMixin.get_form_class, but using the right form base class.
if self.form_class:
return self.form_class
else:
model = _get_view_model(self)
if self.fields:
fields = self.fields
return modelform_factory(model, form=TranslatableModelForm, fields=fields)
else:
return modelform_factory(model, form=TranslatableModelForm)
def get_form_kwargs(self):
"""
Pass the current language to the form.
"""
kwargs = super().get_form_kwargs()
# The TranslatableAdmin can set form.language_code, because the modeladmin always creates a fresh subclass.
# If that would be done here, the original globally defined form class would be updated.
kwargs["_current_language"] = self.get_form_language()
return kwargs
# Backwards compatibility
# Make sure overriding get_current_language() affects get_form_language() too.
def get_form_language(self):
return self.get_current_language()
The tabs should look like this:
But now it looks like this:
If someone have a similiar exprience, feel free to write your opinion

In the settings.py file find PARLER_LANGUAGES settings and change None to SITE_ID.
PARLER_LANGUAGES = {
1: (
{'code': 'en', }, # English
{'code': 'ru', }, # Russian
),
'default': {
'fallbacks': ['en'],
'hide_untranslated': False,
}
}

Related

django - choices from __init__ are not loading in the form

I am using __init__ to build my form choices from parameters passed from the view. It looks like my choices are built correctly when I do print(choices), but the form is not loading any choices. There isn't even a widget for it showing. I do not get any errors. I've used similar code in other views which worked, which is one reason why this one is really confusing me.
I did see that print("ok") never gets printed to the shell, while print("else") does get printed
view
def newobjtoassess(request, assess_pk):
user = request.user
assessment = Assessment.objects.get(pk=assess_pk)
course_pk = assessment.course.pk
context['assessment'] = assessment
form = ObjToAssessmentForm(user=user, course_pk=course_pk)
if request.method == 'POST':
print("ok")
form = ObjToAssessmentForm(request.POST, user=user, course_pk=course_pk)
if form.is_valid():
f = form.cleaned_data
objective = f.get('objective')
assessment.objectives.add(objective)
assessment.save()
return HttpResponseRedirect(reverse('gradebook:assessupdate', args=[assess_pk]))
else:
context['form'] = form
return render(request, "gradebook/newobjtoassess.html", context)
else:
print("else")
form = ObjToAssessmentForm(user=user, course_pk=course_pk)
return render(request, "gradebook/newobjtoassess.html", context)
form
class ObjToAssessmentForm(forms.Form):
objective = forms.ChoiceField(label='Learning Objective', choices=[])
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
my_course = kwargs.pop('course_pk')
super(ObjToAssessmentForm, self).__init__(*args, **kwargs)
choices=[(o.id, str(o)) for o in Objective.objects.filter(user=user, course=my_course)]
print(choices)
self.fields['objective'] = forms.ChoiceField(choices=choices)
template
{% extends 'base-g.html' %} {% load static %} {% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row">
<div class="col">
<p>Course: {{ assessment.course }}</p>
</div>
</div>
<div class="row">
<div class="col">
<p>{{ assessment.assessment_name }}</p>
</div>
</div>
<div class="row">
<div class="col">
<form method="post">{% csrf_token %}
{{ form|crispy }}
<input type = "submit" value="Update">
</form>
</div>
</div>
</div>
{% endblock content %}
I got mixed up with the indentation on the else. As well, I needed to pass the form to the template in the else.
# the last (2nd of the two) else in the view above
else:
form = ObjToAssessmentForm(user=user, course_pk=course_pk)
context['form'] = form
return render(request, "gradebook/newobjtoassess.html", context)
The view can be cleaned up a bit, but this solves the problem.

Python Django - Pass initial value into bound form

I'm new to Python Django and I'm trying to set up a bound form with three fields where one field (which will be read only) takes an initial value and displays it on the form; the other two fields will be populated by the user when they completes the form. But when I try to submit the form, the form.is_valid returns false, and I'm not sure what I did wrong. Can anyone please help?
Thanks
Here's the code:
views.py
class AddKeyAccompView(TemplateView):
template_name = 'AddKeyAccomp.html'
def get(self, request, Program):
username = request.user.username
form = AddKeyAccompForm(request, Program=Program)
return render(request, self.template_name, {'form': form
,'title':'Add New Key Accomplishment'
,'User': username
})
def post(self, request, Program):
username = request.user.username
form = AddKeyAccompForm(request.POST, Program=Program)
if form.is_valid():
Name = form.cleaned_data['Name']
Description = form.cleaned_data['Description']
form.save()
form = AddKeyAccompForm(request, Program=Program)
return HttpResponseRedirect(reverse('ModInit', args=[Program]))
else:
return HttpResponse('invalid form')
args = {'form': form
,'title':'Add New Key Accomplishment'
,'User': username
,'Name': Name
,'Desc': Description
,'Program': Program
}
return render(request, self.template_name, args)
forms.py
class AddKeyAccompForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
self.program = kwargs.pop('Program')
super(AddKeyAccompForm, self).__init__(*args, **kwargs)
self.fields['Program'].initial = self.program
class Meta:
model = Key_Accomplishments
fields = ('Name', 'Description', 'Program',)
widgets = {
'Name': forms.TextInput(attrs={'required':True, 'class':'form-control'})
,'Description': forms.Textarea(attrs={'required': True, 'class':'form-control'})
,'Program': forms.TextInput(attrs={'readonly':'readonly', 'class':'form-control'})
}
html file
{% extends 'base.html' %}
<!DOCTYPE html>
<html>
<head>
{% block head %}
<title>New Key Accomplishment</title>
{% endblock %}
</head>
<body>
{% block body %}
<div class="container">
<ol class="breadcrumb my-4">
<li class="breadcrumb-item active">
<center>{{ title }}</center>
</li>
</ol>
</div>
<br>
<div class="offset-3 col-md-6">
<form method="post">
{% csrf_token %}
<div class="form-group" style="padding-left:10%;">
{% for field in form %}
<label>{{ field.label_tag }}</label>
{{ field }}
<br>
{% endfor %}
</div>
<div class="btn" style="padding-left: 10%;">
<button type="submit">Submit</button>
</div>
</form>
</div>
{% endblock %}
</body>
</html>

Using modelformset_factory in a Django Class Based View

I am building a view that will let me update multiple fields on multiple objects at the same time. I'm doing this using ModelFormSet & modelformset_factory.
The template will be a table of forms with the object name to the left of the fields (see image below).
I found this example, but I am stuck on how to implement the class based view & template.
My Formset
class BaseFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseFormSet, self).__init__(*args, **kwargs)
self.queryset = Reference.objects.filter(
start__isnull=True)
ReferenceFormSet = modelformset_factory(
Reference,
fields=('start', 'end'),
formset=BaseFormSet,
extra=0)
My View
class ReferenceFormSetView(LoginRequiredMixin, SuperuserRequiredMixin, FormView):
model = Reference
form_class = ReferenceFormSet
template_name = "references/references_form.html"
def form_valid(self, form):
for sub_form in form:
if sub_form.has_changed():
sub_form.save()
return super(ReferenceFormSetView, self).form_valid(form)
My Template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h1>{{ headline }}</h1>
<div class="row">
<form action="" method="post">
{% crispy form %}
<div class="">
<input type="submit" value="Submit" />
</div>
</form>
</div>
</div>
{% endblock content %}
Questions
The view seems odd with the Formset in the form_class. Is there a better way to handle this?
How can I access the instance name to display in the form?
I found a solution using a package called django-extra-views.
There is a class called ModelFormSetView which does exactly what I wanted. Here is my implementation (simplified) for others to use -
My View
class ReferenceFormSetView(ModelFormSetView):
model = Reference
template_name = "references/references_form.html"
fields = ['start', 'end']
extra = 0
def get_queryset(self):
return self.model.objects.all()
def get_success_url(self):
return reverse('references:formset')
def formset_valid(self, formset):
"""
If the formset is valid redirect to the supplied URL
"""
messages.success(self.request, "Updated")
return HttpResponseRedirect(self.get_success_url())
def formset_invalid(self, formset):
"""
If the formset is invalid, re-render the context data with the
data-filled formset and errors.
"""
messages.error(self.request, "Error dummy")
return self.render_to_response(self.get_context_data(formset=formset))
My Template
<form class="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="">
{% for field in form %}
{{ field }}
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Save</button>
</form>

Data not rendering using Ajax and the rest framework

I am using the rest frame in my django app in order to access some data.
For some reason, I do not manage to access it. I have no error message but the data is not showing up.
my views are.
class TeamChartData(APIView):
queryset = MyUser.objects.all()
serializer_class = MyUserSerializer, #ProjectSerializer
permission_classes = []
http_method_names = ['get',]
def get_serializer_class(self):
return self.serializer_class
def get(self, request, format=None, *args, **kwargs):
cohesiveness_score = get_team_cohesivenss_score(self)
data = {
"cohesiveness_score":cohesiveness_score[0],
}
return Response(data)
my Html :
{% extends 'base.html' %}
{% load static %}
{% block body %}
<div class="container paddingtop80 marginbottom30">
<div class="row">
{% if project.has_member_responses %}
<div class="col-8">
<div class="jumbotron greenback">
<h4>Welcome to the Project test "{{ project.name }}" Detail page</h4>
</div>
</div>
<div class="col-4">
<div class="jumbotron greenback">
<div class="inner-score">
<h6>Team Score</h6>
<h4>{{cohesiveness_score}}</h4>
</div>
</div>
</div>
{% else %}
<div class="col">
<div class="jumbotron greenback">
<h4>Welcome to the Project "{{ project.name }}" Detail page</h4>
</div>
</div>
{%endif%}
</div>
<!-- case 1 = if there is not team created or linked -->
{% if project.team_id == None %}
{% include "projectdetailtemp/noteamcreated.html" %}<
<!-- case 2 = if there is a team created but no team members -->
{% elif project.team_id and project.team_id.members.count == 0 %}
{% include "projectdetailtemp/teamnomember.html" %}<
<!-- any other situation -->
{% else %}
{% include "projectdetailtemp/other.html" %}
{% endif %}
{% if project.has_member_responses %}
{% include "survey/team_dashboard.html" %}
{% else %}
<div class="jumbotron redback">
We are still waiting for all members to answer the team questionnaire
</div>
{% endif %}
</div>
{% endblock%}
<script>
var endpoint = 'api/chart/data2'
$.ajax({
method: "GET",
url: endpoint,
success: function(data){
console.log(data)
cohesiveness_score = data.cohesiveness_score
}
})
</script>
when I go to my endpoint I can see the data that I am trying to access to the problem does not come from the view.py I guess..
The data I am trying to access is :{{cohesiveness_score}}
Could someone help me ?
edited:
the view in question :
def TeamSelect(request):
#import pdb; pdb.set_trace()
if request.method == "POST":
select_form = EditSelectTeam(request.user, request.POST)
if select_form.is_valid():
data = select_form.cleaned_data['team_choice']
obj2 = Project.objects.filter(project_hr_admin=request.user)
obj3 = obj2.latest('id')
if obj3.team_id == None:
obj3.team_id = data
obj3.save()
obj4 = obj3.team_id
obj5 = obj4.members.all()
for i in obj5:
current_site = get_current_site(request)
message = render_to_string('acc_join_email.html', {
'user': i.first_name,
'domain':current_site.domain,
})
mail_subject = 'You have been invited to SoftScores.com please LogIn to get access to the app'
to_email = i.email
email = EmailMessage(mail_subject, message, to=[to_email])
email.send()
messages.success(request, 'test')
return HttpResponseRedirect(reverse('website:ProjectDetails', kwargs={'pk':obj3.id}))
else:
print('this project has already a team')
else:
print('Non Valid form')
else:
select_form = EditSelectTeam(request.user)
return render(request,'link_project.html',
{'select_form':select_form })
the url :
url(r'^project/(?P<pk1>[0-9]+)/linkteam2/$', views.TeamSelect, name='team_select'),
the link :
<span class="fa fa-link"></span> Link an existing team
By default rendering class of Views in Django rest is JSONRenderer. You need to define your rendering class to HTML and template name. After this, yiur data will be rendered in HTML.
from rest_framework.renderers import TemplateHTMLRenderer
class TeamChartData(APIView):
queryset = MyUser.objects.all()
serializer_class = MyUserSerializer, #ProjectSerializer
permission_classes = []
http_method_names = ['get',]
renderer_classes = [TemplateHTMLRenderer]
template_name = 'template_name.html'

Edit Model data using ModelForm: ModelForm validation error

I am working on my first django app. I am building an app that allows the user to rate beer. I want my user to be able to edit an entry they've already created. I take them to a ModelForm, and ask for their entry. When the POST method is called, my data is invalid. Here is my model.py:
from django.db import models
class Rating(models.Model):
beer_name = models.TextField()
score = models.DecimalField(max_digits=2, decimal_places=1)
notes = models.TextField(blank=True)
brewer = models.TextField(blank=True)
and forms.py:
from django import forms
from ratings.models import Rating
class RatingForm(forms.ModelForm):
class Meta:
model = Rating
fields = ['beer_name', 'score', 'notes', 'brewer']
Here is the views.py of my edit function:
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
if request.method == "POST":
form = RatingForm(request.POST, instance=rating)
if form.is_valid():
form.save()
return redirect(home)
else:
return HttpResponse("Invalid entry.")
else:
context = {'form': rating}
form = RatingForm(instance=rating)
return render(
request,
'ratings/entry_def.html',
context
)
However, every time the POST is called I get an "Invalid entry." HttpResponse, meaning my form.is_valid() is being returned False. Here is my template:
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2>Edit Rating</h2>
<form role="form" method="post">
{% csrf_token %}
<p>Beer Name: <textarea>{{ form.beer_name }}</textarea></p>
<p>Score: <input type="text" name="BeerScore" value="{{ form.score }}"></p>
<p>Notes: <textarea>{{ form.notes }}</textarea></p>
<p>Brewer: <textarea>{{ form.brewer }}</textarea></p>
<p><button type="submit" class="save btn btn-primary">Save</button></p>
<p><button type="reset" class="btn btn-primary">Cancel</button></p>
</form>
</div>
</div>
</div>
{% endblock %}
So when I press my Save button, I am getting the response. Here is my edit url in urls.py:
urlpatterns = [
...
url(r'rating/edit/(?P<row_id>[0-9]+)/$', edit , name='rating-edit'),
]
You're wrapping fields in other fields which don't have name attributes. This is most likely causing the values to be excluded from the request.POST data.
Additionally, Django form fields all have a corresponding HTML widget. So there's really no need to render the HTML by hand, unless you need to.
Change your template code to:
<p>
{{ form.beer_name.label }}: {{ form.beer_name }}
{% if form.beer_name.errors %}
<br />{{ form.beer_name.errors }}
{% endif %}{# repeat for other fields as needed #}
</p>
<p>{{ form.score.label }}: {{ form.score }}</p>
<p>{{ form.notes.label }}: {{ form.notes }}</p>
<p>{{ form.brewer.label }}: {{ form.brewer }}</p>
<p><button type="submit" class="save btn btn-primary">Save</button></p>
<p><button type="reset" class="btn btn-primary">Cancel</button></p>
If you need to change the widget, do so at the form class level:
class RatingForm(forms.ModelForm):
class Meta:
model = Rating
def __init__(self, *args, **kwargs):
super(RatingForm, self).__init__(*args, **kwargs)
self.fields['notes'].widget = forms.Textarea()
This way, Django manages the attributes and binding for you.
Your view can also use some cleanup:
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
form = RatingForm(request.POST or None, instance=rating)
if request.method == "POST" and form.is_valid():
form.save()
return redirect(home)
context = {'form': rating}
return render(request, 'ratings/entry_def.html', context)