django: display UpdateView in language defined by the object - django

I have a django project where I would like to display an UpdateView of a ModelForm in the language which is defined by the model itself. I would like to avoid using i18n patterns in the URL.
The object to be displayed is identified via an UUID provided in the URL of the form
http://name.tld/submit/55dbf53c-9364-4938-873f-6abad2e4ed45
I have:
Model:
class MyModel(models.Model):
unique_id = models.UUIDField(
default=uuid.uuid4,
editable=False,
unique=True,
verbose_name=_("UUID Field")
)
language = models.CharField(
default = "en",
max_length=7,
choices=settings.LANGUAGES,
verbose_name=_("Selected language")
)
ModelForm:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
localized_fields = '__all__'
fields = '__all__'
UpdateView:
class MyModelUpdate(UpdateView):
model = MyModel
form_class = MyModelForm
template_name_suffix = '_form'
def get_object(self, **kwargs):
# get the uuid and find the corresponding object
obj = MyModel.objects.filter(unique_id=self.kwargs.get('uuid')).first()
if not obj or obj.is_answered:
raise Http404("Page not found")
else:
translation.activate(obj.language)
return obj
def get_success_url(self):
return reverse('submit-success')
Template:
{% load i18n %}
{% trans alert %}
<h2>{% trans "My great translated UpdateView" %}</h2>
<form action="" method="post">
{% csrf_token %}
{% form %}
</form>
However, despite providing the translated strings in the PO files (created using django-admin makemessages and django-admin compilemessages the page always shows up in English (my browser language, that is).
What am I missing?

Related

Django custom Management: Unable for an "administrator" to update an user

I'm currently creating a django customer management/admin interface for a web application(I know of the built-in one, but as part of this project, a custom one needs to be created).
I'm supposed to be able to create/update/delete users from this interface, while connected as a Manager/Admin.
While connected as a manager/admin I can successfully create or delete an user, I'm unable to update one (I keep getting the error that the user "already exists")
Help or the wright direction to follow would be much apreciated as I've been struging with this for a while and am blocked atm.
Hereunder the code.
(models.py) class UserProfile (one to one to built-in Django User class)
class UserProfile(models.Model):
"""
The Class UserProfile extends the built-in "User" class of Django
Used here to add extra fields in the forms
"""
user = models.OneToOneField(User, on_delete=models.CASCADE,verbose_name="User")
vpn_enabled = models.BooleanField(default=False, verbose_name="VPN Enabled")
language = models.CharField(max_length=2, choices=LANGUAGES, default='EN', verbose_name="Language")
birth_date = models.DateField(null=True,blank=True, verbose_name="Birth Date")
address = models.CharField(max_length=200, null=True, blank=True, verbose_name="Address")
postal_code = models.CharField(max_length=50,null=True, blank=True, verbose_name="Postal Code")
country = models.CharField(max_length=20, blank=True, null=True, verbose_name="Country")
class Meta:
verbose_name = 'User Profile'
verbose_name_plural = 'User Profiles'
def __str__(self):
return self.user.username
views.py
#group_required('Administrator', 'Manager')
def update_user(request, id):
user = User.objects.get(id=id)
user_update_form = UserUpdateForm(request.POST or None, instance = user)
user_profile_update_form = UserProfileUpdateForm (request.POST or None, instance = user.userprofile)
if user_update_form.is_valid() and user_profile_update_form.is_valid():
user_update_form.save()
user_profile_update_form.save()
return redirect("manager_home")
context = {
'user_update_form': user_update_form,
'user_profile_update_form': user_profile_update_form,
}
return render (request, "update_user.html", context)
forms.py
class UserUpdateForm(forms.ModelForm):
"""UserUpdateForm custom made class"""
class Meta:
""" Meta definitioon of UserUpdateForm"""
model = User
fields = [
'username',
'last_name',
'first_name',
'email',
]
class UserProfileUpdateForm(forms.ModelForm):
'''UserProfileUpdateForm custom made class'''
class Meta:
"""Meta definition of UserProfileUpdateForm"""
model = UserProfile
fields = [
'language',
'birth_date',
'address',
'postal_code',
'country',
]
template
<!-- template update_user.html -->
{% extends 'base.html' %}
{% block title %} Edit User {% endblock %}
{% block content %}
<form method="POST" class="post-form" action="{% url 'update_user' user.id %}">
{% csrf_token %}
{{user_update_form.as_p}}
{{user_profile_update_form.as_p}}
{{user_update_form.errors}}
<hr>
<button type="submit">Update User</button>
</form>
{% endblock %}
result (while trying to update, for example, user country)
enter image description here
The problem is with the view. If you are using function as a view you need to say explicitly what you do in case of the post request, otherwise everything will be processed as GET request, and this is not something that you want.
#group_required('Administrator', 'Manager')
def update_user(request, id):
if request.method == 'GET':
# get request processing code
# you are missing POST request processing part
if request.method == 'POST':
# post request processing code

How to use ModelChoiceField in DRF?

I am trying to convert my form that was written earlier to django rest serializer but it does't work. Could you help me to solve this problem please?
this is my form:
class TripSearchForm(forms.Form):
departure = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
destination = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
How to built proper serializer?
class SearchSerializer(serializers.Serializer):
departure = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='departure')
destination = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='destination')
Assuming you have model Country
class Country(models.Model):
name = models.CharField(max_length=60, blank=True, default='')
You could write a serializers based on that
class CountryField(serializers.PrimaryKeyRelatedField):
def display_value(self, instance):
return instance.name
class CountrySerializer(serializers.ModelSerializer):
country = CountryField(queryset=Country.objects.all())
class Meta:
model = Country
fields = ('name', )
class DesiredSerializer(serializers.Serializer):
country = ColorSerializer()
Now you should be able to use your desired serialized to render choices from model either as html form for instance or whatever logic fits you better
if you want it as form
#views.py
def get(self, request):
serializer = DesiredSerializer()
return Response({ 'serializer': serializer }, template_name='my_model_choices_form.html')
<!-- my_model_choices_form.html -->
{% load rest_framework %}
<form action="..." method="POST">
{% csrf_token %}
{% render_form serializer %}
</form>
Now if you'll create instance of Country with some name it will be shown in select dropdown, display_value function can be used to customize the option output.
Hope that helps

How to pre-fill a form and its related inlines with values of another model’s object?

I have a generic CreateView which displays the model and a related ForeignKey model as a form with inlines. Using a similar model with a similar related ForeignKey model as a source - how can i pre-fill the original form and get only the proper amount of inlines according to an object from the source?
The closest i got to a solution is using CreatWithInlinesView from django-extra-views which gives me an empty form with the related model as inlines. But how do i get the data from an existing object of another model into this form with the proper amount of inlines that the object needs?
models.py
class Offers(models.Model):
reference = models.CharField(unique=True, max_length=10)
user = models.ForeignKey(User)
…
class OfferDetail(models.Model):
offer = models.ForeignKey(Offers, related_name='offerdetails')
product_name = models.CharField(max_length=255)
…
# where the data for prefilling comes from:
class Orders(models.Model):
reference = models.CharField(unique=True, max_length=10)
…
class OrderDetail(models.Model):
order = models.ForeignKey(Orders, related_name=‘orderdetails')
product_name = models.CharField(max_length=255)
…
urls.py
url(r'^offer/(?P<reference>[A-Z]{9})/$', views.MyOfferView.as_view(), name=‘somename’),
now if a user visits url offer/REFERENCE he should see a form that creates an offer but is pre-filled from the order object that has the reference REFERENCE
without pre-filled data from the order object it is working like this (using django-extra-views)
views.py
from extra_views import CreateWithInlinesView, InlineFormSet
class DetailsInline(InlineFormSet):
model = OfferDetail
class MyOfferView(CreateWithInlinesView):
model = Offers
inlines = [DetailsInline, ]
template_name = ‘someapp/somename.html'
success_url = ‘/someurl’
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(MyOfferView, self).dispatch(*args, **kwargs)
somename.html
<form action="" method="post">
{% csrf_token %}
{{ form|crispy }}
{% for i in inlines %}
{{ i|crispy }}
{% endfor %}
<input type="submit" value="Submit" class="btn btn-primary"/>
</form>
You may find this documentation helpful:
https://docs.djangoproject.com/en/1.8/ref/forms/api/#dynamic-initial-values

Why does the parent form not show when I render the inline_formset factory?

I am attempting to use a class-based view to display information about a tool, and then add tags to that view, mostly to teach myself how to use inline_formsets. The problem that I am having is how to inject the child object's form into the template.
The problem is that the child's formsets are appearing, but there is no parent form displaying from the template.
The result is that the parent form doesn't show -
Ultimately, this is a "What am I doing wrong in Django?" question.
The model is very simple - Tool has a few fields that define my parent object, and tag is a child that is related to it:
models.py
from django.db import models
class Tool(models.Model):
content_id = models.CharField(
primary_key=True,
help_text="Confluence ID of the tool document",
max_length=12
)
tool_name = models.CharField(
max_length=64,
help_text="Short name by which the tool is called",
)
purpose = models.TextField(
help_text="A one sentence summary of the tools reason for use."
)
body = models.TextField(
help_text="The full content of the tool page"
)
last_updated_by = models.CharField(
max_length=64
)
last_updated_at = models.DateTimeField()
def __unicode__(self):
return u"%s content_id( %s )" % (self.tool_name, self.content_id)
class ToolTag(models.Model):
description = models.CharField(
max_length=32,
help_text="A tag describing the category of field. Multiple tags may describe a given tool.",
)
tool = models.ForeignKey(Tool)
def __unicode__(self):
return u"%s describes %s" % (self.description, self.tool)
I am using standard Class-based forms:
forms.py
from django.http import HttpResponseRedirect
from django.views.generic import CreateView
from django.views.generic import DetailView, UpdateView, ListView
from django.shortcuts import render
from .forms import ToolForm, TagsFormSet
from .models import Tool
TagsFormSet = inlineformset_factory(Tool, ToolTag, can_delete='True')
class ToolUpdateView(UpdateView):
template_name = 'tools/tool_update.html'
model = Tool
form_class = ToolForm
success_url = 'inventory/'
views.py
def call_update_view(request, pk):
form = ToolUpdateView.as_view()(request,pk=pk)
tag_form = TagsFormSet()
return render( request, "tools/tool_update.html",
{
'form': form,
'tag_form': tag_form,
'action': "Create"
}
)
And my template is as follows:
tool_update.html
{% block content %}
<form action="/update/" method="post">
{% csrf_token %}
<DIV>
Tool Form:
{{ form.as_p }}
</DIV>
<DIV>
Tag Form:
{{ tag_form.as_p }}
</DIV>
<input type="submit" value="Submit" />
</form>
{% endblock %}
This line:
form = ToolUpdateView.as_view()(request,pk=pk)
makes no sense at all. A view is not a form, and you can't use it as one. You should just do form = ToolForm(). Although note you also need some code to process the form submission itself, from the request.POST data.

django: How to use inlineformset within the formwizard?

I'm displaying two separate sample projects. The first is a Contact related and shows the principle of using the formwizard. The second is an ingredients to recipes related project which shows how to use inlines within a form. I want inlines to be in my formwizard the same way they work in a normal form.
I have a formwizard multistep form working. It is based off the example here. I've changed it slightly to use modelform.
models.py
from django.db import models
# Create your models here.
class Contact(models.Model):
subject = models.CharField(max_length=50)
sender = models.EmailField()
def __unicode__(self):
return self.subject
class Contact2(models.Model):
message = models.TextField(max_length=500)
def __unicode__(self):
return self.message
forms.py
class ContactForm1(forms.ModelForm):
class Meta:
model = Contact
class ContactForm2(forms.ModelForm):
class Meta:
model = Contact2
class ContactWizard(FormWizard):
#property
def __name__(self):
return self.__class__.__name__
def done(self, request, form_list):
# do_something_with_the_form_data(form_list)
return HttpResponseRedirect('/done/')
urls.py
(r'^contact/$', ContactWizard([ContactForm1, ContactForm2])),
Separately I have inlines being generated into another form. I'm doing this via inlineformset_factory in my view. This is not connected to the formwizard example above. This is an ingredients to recipes example.
I'm doing this like:
views.py
def add(request):
IngredientFormSet = inlineformset_factory(Recipe, Ingredient,
fk_name="recipe",
formfield_callback=curry(ingredient_form_callback, None))
if request.method == 'POST':
form = RecipeForm(request.POST)
formset = IngredientFormSet(request.POST)
if form.is_valid() and formset.is_valid():
recipe = form.save()
formset = IngredientFormSet(request.POST, instance=recipe)
formset.save()
return redirect("/edit/%s" % recipe.id)
else:
form = RecipeForm()
formset = IngredientFormSet()
return render_to_response("recipes_add.html", {"form":form, "formsets":formset}, context_instance=RequestContext(request))
recipes_add.html
<form method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<hr>
<h3>Ingredients</h3>
<div class="inline-group">
<div class="tabular inline-related last-related">
{{ formsets.management_form }}
{% for formset in formsets.forms %}
<table>
{{ formset }}
</table>
{% endfor %}
</div>
</div>
<p class="success tools">Add another row</p>
<input type="submit" value="Add">
</form>
How can I get the inlines to work within my formwizard multistep form?
The models.py now looks like this because I want books to be inlines to contact. I want the inlines to be on the first step of my formwizard. Then go through to step 2 and finish.
from django.db import models
# Create your models here.
class Contact(models.Model):
subject = models.CharField(max_length=50)
sender = models.EmailField()
def __unicode__(self):
return self.subject
class Contact2(models.Model):
message = models.TextField(max_length=500)
def __unicode__(self):
return self.message
class Book(models.Model):
author = models.ForeignKey(Contact)
title = models.CharField(max_length=100)
The formwizard included in Django (below version 1.4) doesn't support formsets. Beginning with version 1.4, there will be a much better implementation (see https://docs.djangoproject.com/en/dev/ref/contrib/formtools/form-wizard/)
Back to your question, if you can't wait for the next Django release - which I assume - you could stick to django-formwizard. The last release (1.0) is api compatible to the upcoming Django formwizard.
With the new formwizard implementation you can use FormSets the same way you use normal Forms.