Django Forms translate standard error message - django

All my translations work except the error messages generated by forms.
This is the start of my form:
class MyForm(forms.Form):
first_name = forms.CharField(label=_("First name"), min_length=3, required=True)
Now the label "First name" translates fine to "Vorname" because I made a .po file and compiled etc..
However when entering only 2 characters the resulting error is in English and not in (this case) German, see screen print below.
I have looked in Github Django repository django/conf/locale/de etc. but could not find the string "Please lengthen etc." there.
So my question is: how do I get this error message to translate? I assume Django has a standard method and that this string is already in some .po file somewhere within the Django project?

Adding an oninvalid attribute to to that field's widget would work.
class MyForm(forms.Form):
first_name = forms.CharField(label=_("First name"), min_length=3, required=True)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['first_name'].widget.attrs={
'oninvalid':"this.setCustomValidity('{German stuff}')",
'oninput':"this.setCustomValidity('')"
}
Edit 1
I've also learned you must add an oninput which then clears the message. I'm looking into other ways, because this solution would always show the same message no matter the validation error- is empty or is less 3 chars = same msg
Edit 2
This requires some extra javascript, but you should only have to do it once.
There might be more error messages, I only tested it on a Select
references = https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#validating_forms_using_javascript
form.py
class MyForm(forms.Form):
first_name = forms.CharField(label=_("First name"), min_length=3, required=True)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['first_name'].widget.attrs={
'oninvalid':"germanValidationMsg(this)",
'oninput':"this.setCustomValidity('')"
}
germanvalidation.js
function germanValidationMsg(e){
// dictionary of validations and their status
console.log(e.validity)
if (e.validity['badInput']){
e.setCustomValidity('German badInput Msg');
} else if (e.validity['patternMismatch']){
e.setCustomValidity('German patternMismatch Msg');
} else if (e.validity['rangeOverflow']){
e.setCustomValidity('German rangeOverflow Msg');
} else if (e.validity['rangeUnderflow']){
e.setCustomValidity('German rangeUnderflow Msg');
} else if (e.validity['stepMismatch']){
e.setCustomValidity('German stepMismatch Msg');
} else if (e.validity['tooLong']){
e.setCustomValidity('German tooLong Msg');
} else if (e.validity['tooShort']){
e.setCustomValidity('German tooShort Msg');
} else if (e.validity['typeMismatch']){
e.setCustomValidity('German typeMismatch Msg');
} else if (e.validity['valueMissing']){
e.setCustomValidity('German valueMissing Msg');
//} else if (e.validity['customError']){
// // customError always seems to be true?
// e.setCustomValidity('German customError Msg');
} else if (e.validity['valid']){
e.setCustomValidity('');
};
};

Related

Django form class and view class connected

Hi in my code(not written by me) i have django form class and views class. I dont know how this is connected each other. Can anyone tell me how this is connected? Also can any one please tell me how this messege : Credential is in use by {0} collections that are turned on and "
"{1} collections that are turned off. Be mindful that over-using " "credentials may result in collecting being rate limited by the " "social media API is displayed, i mean if i need to change the alignment of this text where i should change?
My code classes are :
from forms.py :
class CollectionTwitterSearch2Form(BaseCollectionForm):
incremental = forms.BooleanField(initial=True, required=False, label=INCREMENTAL_LABEL, help_text=INCREMENTAL_HELP)
def __init__(self, *args, **kwargs):
super(CollectionTwitterSearch2Form, self).__init__(*args, **kwargs)
self.helper.layout[0][5].extend(('incremental',))
if self.instance and self.instance.harvest_options:
harvest_options = json.loads(self.instance.harvest_options)
if "incremental" in harvest_options:
self.fields['incremental'].initial = harvest_options["incremental"]
def save(self, commit=True):
m = super(CollectionTwitterSearch2Form, self).save(commit=False)
m.harvest_type = Collection.TWITTER_SEARCH_2
harvest_options = {
"incremental": self.cleaned_data["incremental"],
}
m.harvest_options = json.dumps(harvest_options, sort_keys=True)
m.save()
return m
from views.py :
def _get_credential_use_map(credentials, harvest_type):
credential_use_map = {}
if harvest_type in Collection.RATE_LIMITED_HARVEST_TYPES:
for credential in credentials:
active_collections = 0
inactive_collections = 0
for collection in credential.collections.all():
if collection.is_on:
active_collections += 1
else:
inactive_collections += 1
if active_collections == 0 and inactive_collections == 0:
credential_use_map[credential.id] = ("", "")
else:
credential_use_map[credential.id] = ("warning",
"Credential is in use by {0} collections that are turned on and "
"{1} collections that are turned off. Be mindful that over-using "
"credentials may result in collecting being rate limited by the "
"social media API.".format(active_collections,
inactive_collections))
return credential_use_map
class CollectionCreateView(LoginRequiredMixin, CollectionSetOrSuperuserPermissionMixin, SuccessMessageMixin,
CreateView):
model = Collection
template_name = 'ui/collection_create.html'
def get_initial(self):
initial = super(CollectionCreateView, self).get_initial()
initial["collection_set"] = CollectionSet.objects.get(pk=self.kwargs["collection_set_pk"])
return initial
def get_context_data(self, **kwargs):
context = super(CollectionCreateView, self).get_context_data(**kwargs)
context["collection_set"] = CollectionSet.objects.get(pk=self.kwargs["collection_set_pk"])
harvest_type = self.kwargs["harvest_type"]
context["harvest_type_name"] = _get_harvest_type_name(harvest_type)
credentials = _get_credential_list(self.kwargs["collection_set_pk"], harvest_type)
context["credentials"] = credentials
context["credential_use_map"] = _get_credential_use_map(credentials, harvest_type)
context["platform"] = Collection.HARVEST_TYPES_TO_PLATFORM[self.kwargs["harvest_type"]]
return context
def get_form_kwargs(self):
kwargs = super(CollectionCreateView, self).get_form_kwargs()
kwargs["coll"] = self.kwargs["collection_set_pk"]
kwargs['credential_list'] = _get_credential_list(self.kwargs["collection_set_pk"], self.kwargs["harvest_type"])
return kwargs
def get_form_class(self):
return getattr(forms, _get_collection_form_class(self.kwargs["harvest_type"]))
def get_success_url(self):
return reverse('collection_detail', args=(self.object.pk,))
def get_success_message(self, cleaned_data):
if self.object.required_seed_count() != 0:
return "New collection added. You can now add seeds."
return "New collection added."
Full code is here in this git : https://github.com/gwu-libraries/sfm-ui/tree/master/sfm/ui
It would be great anyone can explain how these two classes and template is connected and how the messege is displayed
The CollectionCreateView class is conected to the Form using the function get_form_class, this function is called by default by the CreateView, in there you can see is calling _get_collection_form_class() and as an argument is passing self.kwargs['harvest_type'] this kwargs is comming from the url declaration. The _get_collection_form_class function is returning the CollectionTwitterSearch2Form when the harvest_type is something like TwitterSearch2. The template is given by the template_name = 'ui/collection_create.html' again this is the default vehaviour. And finally for the message this is using SuccessMessageMixin.

I'm trying to override a django formtools wizard form widget, but it is just using the standard widget

I have the following form
class ProviderSignUp1(forms.ModelForm):
class Meta:
model = models.Provider
fields = [
'childcare_type_informal',
'childcare_type_accredited',
]
wigdets = {
'childcare_type_informal': PatchRadioSelect(
attrs={
'class':'form-control'
}
),
'childcare_type_accredited': PatchRadioSelect(
attrs={
'class':'form-control'
}
)
}
def clean(self):
cleaned_data = self.cleaned_data
if cleaned_data['childcare_type_informal'] == True and cleaned_data['childcare_type_accredited'] == True:
raise ValidationError("You must select only one type of childcare")
if cleaned_data['childcare_type_informal'] == False and cleaned_data['childcare_type_accredited'] == False:
raise ValidationError("You must select at least one type of childcare")
return super().clean()
The widget is defined as
from django.forms.widgets import RadioSelect
class PatchRadioSelect(RadioSelect):
template_name = 'userprofiles/form_widgets/radio.html'
option_template_name = 'userprofiles/form_widgets/radio_option.html'
And my wizard is:
PROVIDER_SIGNUP_TEMPLATES = {
'page0': 'userprofiles/provider_signup_wizard/page0.html',
'page1': 'userprofiles/provider_signup_wizard/page1.html',
'page2': 'userprofiles/provider_signup_wizard/page2.html',
'page3': 'userprofiles/provider_signup_wizard/page3.html',
'page4': 'userprofiles/provider_signup_wizard/page4.html',
'page5': 'userprofiles/provider_signup_wizard/page5.html',
'page6': 'userprofiles/provider_signup_wizard/page6.html',
'page7': 'userprofiles/provider_signup_wizard/page7.html',
'page8a': 'userprofiles/provider_signup_wizard/page8.html',
'page8b': 'userprofiles/provider_signup_wizard/page8.html',
'page9': 'userprofiles/provider_signup_wizard/page9.html',
'page10': 'userprofiles/provider_signup_wizard/page10.html',
}
PROVIDER_SIGNUP_FORMS = [
("page0", forms.ProviderSignUp0),
("page1", forms.ProviderSignUp1),
("page2", forms.ProviderSignUp2),
("page3", forms.ProviderSignUp3),
("page4", forms.ProviderSignUp4),
("page5", forms.ProviderSignUp5),
("page6", forms.ProviderSignUp6),
("page7", forms.ProviderSignUp7),
("page8a", forms.ProviderSignUp8a),
("page8b", forms.ProviderSignUp8b),
("page9", forms.ProviderSignUp9),
("page10", forms.ProviderSignUp10),
]
def accredited_only_condition(wizard):
cleaned_data = wizard.get_cleaned_data_for_step('page1') or {}
return cleaned_data.get('childcare_type_accredited', True)
def informal_only_condition(wizard):
cleaned_data = wizard.get_cleaned_data_for_step('page1') or {}
return cleaned_data.get('childcare_type_informal', True)
class ProviderSignUpWizard(SessionWizardView):
form_list = PROVIDER_SIGNUP_FORMS
condition_dict = {
'page2': accredited_only_condition,
'page8a': accredited_only_condition,
'page8b': informal_only_condition,
'page9': informal_only_condition,
}
def get_form_instance(self, step):
if step == 'page4':
return self.instance_dict.get(step, self.request.user)
return self.instance_dict.get(step, self.request.user.provider)
def get_template_names(self):
return [PROVIDER_SIGNUP_TEMPLATES[self.steps.current]]
def done(self, form_list, **kwargs):
provider_instance = models.Provider.objects.get(id=self.request.user.provider.id)
user_instance = models.User.objects.get(id=self.request.user.id)
for form in form_list:
provider_instance = construct_instance(form, provider_instance, form._meta.fields, form._meta.exclude)
user_instance = construct_instance(form, user_instance, form._meta.fields, form._meta.exclude)
provider_instance.save()
user_instance.save()
return redirect(self.request.user.get_provider_profile_url())
def post(self, *args, **kwargs):
form = self.get_form(data=self.request.POST, files=self.request.FILES)
if form.is_valid():
form.save()
if 'services' in form.cleaned_data:
models.Provider.objects.get(id=self.request.user.provider.id).services.set(form.cleaned_data['services'])
if 'features' in form.cleaned_data:
models.Provider.objects.get(id=self.request.user.provider.id).features.set(form.cleaned_data['features'])
if 'informal_phrases' in form.cleaned_data:
models.Provider.objects.get(id=self.request.user.provider.id).informal_phrases.set(form.cleaned_data['informal_phrases'])
return super().post(*args, **kwargs)
My widget is being ignored and the standard widget is being used. Please note, I seem unable to change any of the widgets from the standard and can only add attributes to the existing widget. What am I doing wrong?
Just in case anyone else comes across the same issue, I found if I define __init__ within the first form in the wizard, I can then override the widgets within __init__ in whichever form I need to use it. But it doesn't work if I don't specify __init__ in the first form.
I think this more a workaround than solution.

Django view if statement

i have choices field. I want in views.py like this;
How can i do?
if gametype == 1:
template='xxx.html'
if gametype == 2:
template='xxx1.html'
views.py
def game_detail(request,tournamentslug,slug,gameslug):
game=get_object_or_404(Model,tournament__slug=tournamentslug,slug=slug,game__slug=gameslug)
context={
'game':game,
}
return render(request,'esports/lolgame.html',context)
models.py
class Model(models.Model):
type_tvt = 1
type_pvp = 2
type_royale=3
types = (
(type_tvt, 'T'),
(type_pvp, 'P'),
(type_royale,'R'),
)
gametype=models.SmallIntegerField(choices=types)
Assuming you have a class-based detail view (and the model is named Game for sanity, not Model as in your example),
class GameView(generic.DetailView):
model = Game
def get_template_names(self):
if self.object.gametype == Game.type_tvt:
return ['xxx.html']
elif self.object.gametype == Game.type_pvp:
return ['xxx2.html']
elif self.object.gametype == Game.type_royale:
return ['xxx3.html']
raise ValueError('invalid game type')
would do the trick – or to simplify using a dict,
class GameView(generic.DetailView):
model = Game
template_names = {
Game.type_tvt: 'xxx.html',
Game.type_pvp: 'xxx2.html',
Game.type_royale: 'xxx3.html',
}
def get_template_names(self):
return [self.template_names[self.object.gametype]] # may raise KeyError
EDIT: for a function-based view, as in the edited question,
template_names = {
Model.type_tvt: "xxx.html",
Model.type_pvp: "xxx2.html",
Model.type_royale: "xxx3.html",
}
def game_detail(request, tournamentslug, slug, gameslug):
game = get_object_or_404(
Model,
tournament__slug=tournamentslug,
slug=slug,
game__slug=gameslug,
)
context = {"game": game}
template_name = template_names[game.gametype]
return render(request, template_name, context)

return render sends to correct location but no data until page reload?

I have a timeclock app for employees to check-in/check-out with which works ok, however when you're on the user-activity page and either check in or check out once it submits the form it sends you back to the user-activity.html page, but no data is loaded in the table and its not loading data from the view. Once you refresh the page the data is there again but I'm not sure why this is happening. I'll attach 2 images, one before the checkin/checkout button is pressed, and one after
and here is my view. Thanks for any help you can give
class ActivityView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
context = {}
toggle = UserActivity.objects.current(request.user)
user_data = UserActivity.objects.filter(user=request.user).order_by('-timestamp')
current_users = UserActivity.objects.order_by('user', '-timestamp').distinct('user')
context['toggle'] = toggle
context['user_data'] = user_data
context['current_users'] = current_users
return render(request, "administration/timesheets/user-activity.html", context)
def post(self, request, *args, **kwargs):
context = {}
toggle = UserActivity.objects.toggle(request.user)
context['toggle'] = toggle
return render(request, "administration/timesheets/user-activity.html", context)
EDIT
Also here is my model...
USER_ACTIVITY_CHOICES = (
('checkin', 'Check In'),
('checkout', 'Check Out'),
)
class UserActivityManager(models.Manager):
def current(self, user):
current_obj = self.get_queryset().filter(user=user).order_by('-timestamp').first()
return current_obj
def toggle(self, user):
last_item = self.current(user)
activity = "checkin"
if last_item is not None:
if last_item.timestamp <= tz.localize(datetime.datetime.now()):
pass
if last_item.activity == "checkin":
activity = "checkout"
obj = self.model(
user=user,
activity=activity
)
obj.save()
return obj
class UserActivity(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
activity = models.CharField(max_length=120, default='checkin', choices=USER_ACTIVITY_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
time_delta = models.DecimalField(decimal_places=2, max_digits=4, default='0.00', blank=True, null=True)
status = models.CharField(max_length=60, null=True, blank=True)
description = models.TextField(blank=True, null=True)
objects = UserActivityManager()
def __unicode__(self):
return str(self.activity)
def __str__(self):
return str(self.activity)
class Meta:
verbose_name = 'User Activity'
verbose_name_plural = "User Activities"
def next_activity(self):
next = "Check in"
if self.activity == "checkin":
next = "Check out"
return next
#property
def current(self):
current = 'Checked Out'
if self.activity == 'checkin':
current = "Checked in"
return current
def clean(self, *args, **kwargs):
if self.user:
user_activities = UserActivity.objects.exclude(
id=self.id
).filter(
user = self.user
).order_by('-timestamp')
if user_activities.exists():
recent_ = user_activities.first()
if self.activity == recent_.activity:
message = "%s is not a valid activity for this user" %(self.get_activity_display())
raise ValidationError(message)
else:
if self.activity != "checkin":
message = "%s is not a valid activity for this user" %(self.get_activity_display())
raise ValidationError(message)
return super(UserActivity, self).clean(*args, **kwargs)
In your post function, I think you have a typing mistake :
toggle = UserActivity.objects.toggle(request.user)
I think that instead, you need :
toggle = UserActivity.objects.current(request.user)

how to work with modelform and multiwidget

Newbie to all this! i'm working on displaying phone field displayed as (xxx)xxx-xxxx on front end.below is my code. My question is 1. all fields are mandatory, for some reason,phone is not behaving as expected.Even if it is left blank its not complaining and 2.how can i test this widget's functionality
class USPhoneNumberWidget(forms.MultiWidget):
def __init__(self,attrs=None):
widgets = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'4'}))
super(USPhoneNumberWidget,self).__init__(widgets,attrs=attrs)
def decompress(self, value):
if value:
val = value.split('-')
return [val[0],val[1],val[2]]
return [None,None,None]
def compress(self, data_list):
if data_list[0] and data_list[1] and data_list[2]:
ph1 = self.check_value(data_list[0])
ph2 = self.check_value(data_list[1])
ph3 = self.check_value(data_list[2])
return '%s''%s''%s' %(ph1,ph2,ph3)
else:
return None
def check_value(self,val):
try:
if val.isdigit():
return val
except:
raise forms.ValidationError('This Field has to be a number!')
def clean(self, value):
try:
value = re.sub('(\(|\)|\s+)','',smart_unicode(value))
m = phone_digits_re.search(value)
if m:
return u'%s%s%s' % (m.group(1),m.group(2),m.group(3))
except:
raise ValidationError('Phone Number is required.')
def value_from_datadict(self,data,files,name):
val_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)]
try:
return val_list
except ValueError:
return ''
def format_output(self,rendered_widgets):
return '('+rendered_widgets[0]+')'+rendered_widgets[1]+'-'+rendered_widgets[2]
class CustomerForm(ModelForm):
phone = forms.CharField(required=True,widget=USPhoneNumberWidget())
class Meta:
model = Customer
fields = ('fname','lname','address1','address2','city','state','zipcode','phone')
In models blank and null are not true.
Any input it highly appreciated.Thanks
Here is the phone field:
phone = forms.CharField(label = 'Phone',widget=USPhoneNumberWidget()
class USPhoneNumberWidget(forms.MultiWidget):
"""
A widget that splits phone number into areacode/next3/last4 with textinput.
"""
def __init__(self,attrs=None):
widgets = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'4','maxlength':'4'}))
super(USPhoneNumberWidget,self).__init__(widgets,attrs=attrs)
def decompress(self, value):
if value:
val = value
return val[:3],val[3:6],val[6:]
return None,None,None
def compress(self, data_list):
if data_list[0] and data_list[1] and data_list[2]:
return '%s''%s''%s' %(data_list[0],data_list[1],data_list[2])
else:
return None
def value_from_datadict(self,data,files,name):
val_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)]
if val_list:
return '%s''%s''%s' %(val_list[0],val_list[1],val_list[2])
def format_output(self,rendered_widgets):
return '( '+rendered_widgets[0]+' )'+rendered_widgets[1]+' - '+rendered_widgets[2]
But depending on how you store the phone# in db 'return' line is to be changed. here I'm accepting it as (xxx)-xxx-xxxx format.In compress it receives ph_0(areacode),ph_1(next 3),ph_2(last4) in that order.but I'm storing it as xxxxxxxxxx.
Firebug helped me understand better about what return values should be. I'll update the answer when i come to know how testing could be done.