I have the following model that links to "user":
class Profile(models.Model):
user = models.OneToOneField(User)
title = models.CharField(max_length=10)
dob = models.DateField(max_length=8)
class Meta:
managed = True
db_table = 'fbf_profile'
I then have the following registration form:
class RegistrationForm(BootstrapModelForm, UserCreationForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
# The default Django user model doesn't require these fields to be set
# but we do.
self.fields['first_name'].required = True
self.fields['last_name'].required = True
self.fields['email'].required = True
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email__iexact=email).exists():
raise ValidationError('There is already an account registered with this e-mail address.')
return email
class Meta:
model = User
fields = ['first_name', 'last_name', 'email', 'username']
I am then able to use them in my templates like so:
<div class="form-group">
<label class="col-sm-5 control-label">{{ form.first_name.label }}:</label>
<div class="col-sm-7">
{{ form.first_name }}
<div class="text-danger">
{% for error in form.first_name.errors %}{{ error }}<br/>{% endfor %}
</div>
</div>
</div>
However, how can I use the "dob" from the Profile model within the template in much the same way as I have done with form.first_name.label and form.first_name above. Many thanks in advance, Alan.
If using modelForms you well need two distinct forms - one for the User model and one for the Profile model. You then display both within the same <form> tag in your template, and validate / save both in your view.
The other solution is to define a plain (non 'model') form with fields for both models, and write the save method by yourself.
Assuming that form is an User object which you're using in the template, you should be able to access the dob field using form.profile.dob.
Many thanks to Bruno who gave the correct answer above. It led me to the following link:
https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#onetoone
I hope it helps someone.
Related
I have a django filter with a dependent drop down to filter car manufactures and models. The models use a charfield and pulls the cars from a db entry.
I would like a place holder to say manufacture and model on their respected fields.
I cant find much online about doing this. The only post I can find relates to using the choice field on the model which wont work for me.
filter
class CarFilterForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['model'].queryset = Post.objects.none()
if 'model_manufacture_id' in self.data:
try:
model_manufacture_id = int(self.data.get('model_manufacture_id'))
self.fields['model_id'].queryset = CarModels.objects.filter(model_manufacture_id=model_manufacture_id)
except (ValueError, TypeError):
pass
class carFilter(django_filters.FilterSet):
class Meta:
model = Post
fields = 'manufacture', 'model'
form = CarFilterForm
html
<form method="get" id="AllCarsForm" data-cars-url="{% url 'ajax-allcars' %}">
{% render_field myFilter.form.manufacture class="cars-filter-widget" %}
{% render_field myFilter.form.model class="cars-filter-widget" %}
<button class="btn btn-primary" type="submit">Search</button>
</form>
models
class Manufactures(models.Model):
manufacture_id = models.AutoField(primary_key=True)
manufacture = models.CharField(max_length=55, default="OEM")
class CarModels(models.Model):
model_id = models.AutoField(primary_key=True)
model = models.CharField(max_length=55)
model_manufacture = models.ForeignKey(Manufactures, on_delete=models.CASCADE)
Try to set the empty_label for the fields:
self.fields['your_field'].empty_label = 'My custom empty label'
The simplest method of doing this is to set the model field default to one that corresponds to your fields.
Example:
class Model(models.Model):
field = models.CharField(max_length=25, choices=CHOICES,
default=DEFAULT, blank=True)
You can also do this in forms:
self.fields['field'].choices = [('', 'Placeholder Text')] + list(
self.fields['field'].choices[1:])
I'm trying to add a placeholder to the username field in the UserCreationForm
but i can't understand how to do it.
I have already change the forms.py file in this way /lib/site-packages/django/contrib/auth/forms.py.
I add a placeholder to password1 and password2 fields and work like this:
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
error_messages = {
'password_mismatch': _("The two password fields didn't match."),
}
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={'placeholder':'Password'}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput(attrs={'placeholder':'Confirmar Password'}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
I can see probably the username field is coming from class meta:
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
from this class but i'm not sure about that
class UsernameField(forms.CharField):
def to_python(self, value):
return unicodedata.normalize('NFKC', super().to_python(value))
I don't understand how add a placeholder to username field
this is my html
<form method="post" action=".">
{% csrf_token %}
{% for field in form %}
{{ field }}<br />
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
{% endfor %}
<input id="submit-signup-btn" type="submit" value="Iniciar"/>
</form>
After a while looking at this, i found a way to do it, this is the way i did it:
in the file /lib/site-packages/django/contrib/auth/forms.py, in class UserCreationForm, under class Meta there is a init function where you can see this line
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus':
True})
i added the placeholder to the end of the line in the update method like this:
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus': True,
'placeholder':'Nome Perfil'})
Based on #Gabriel costa answer, I came up with this solution:
class AccountCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.update({'placeholder':_('Username')})
self.fields['email'].widget.attrs.update({'placeholder':_('Email')})
self.fields['password1'].widget.attrs.update({'placeholder':_('Password')})
self.fields['password2'].widget.attrs.update({'placeholder':_('Repeat password')})
Other solutions don't work in this scenario.
I had a similiar problem before, this is how i solved it :
class Meta:
......
widgets = {
'username': forms.fields.TextInput(attrs={'placeholder': 'random stuff'})
}
or internally , e.g
city = forms.CharField(widget=forms.TextInput(attrs={'placeholder':'text'}))
You can also define widgets in form's Meta class.
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
widgets = {
'username': form.fields.TextInput(attrs={'placeholder': 'Your text for placeholder'})
}
I am trying to build a form and I am not sure how this should be done correctly. These are my models:
class Country(models.Model):
name = models.CharField(max_length=250, null=False, blank=False, unique=True)
twocode = models.CharField(max_length=5, null=False, blank=False, unique=True)
class GeoBonus(models.Model):
name = models.CharField(max_length=250)
country = models.ForeignKey(Country, related_name='geo_bonuses')
bookmaker = models.ForeignKey(Bookmaker, related_name='geo_bonuses')
Bookmaker has bonuses, different for each country. For example:
Bookmaker has bonuses:
Slovakia: "eligible for 100% up to $200"
Afghanistan: "eligible for 100% up to €100!"
USA: "restricted country"
...
And I want to save text in quotes as name in GeoBonus. Of course I can write use simple model form, but I would have submit form 248 time(for each country). I would like to show all fields for each country.
If name is blank, GeoBonus will not be created.
If name is not blank, create GeoBonus object.
This is what it should look like:
How code in forms.py and views.py will look like? I also need to edit fields.
I tried to manually create new fields for countries:
<form method="post" action="" class="wide">
{% csrf_token %}
{%bootstrap_form form %}
<div class="form-group">
{%for country in countries%}
<label class="control-label" for="{{country.twocode}}">{{country}}</label>
<input class="form-control" id="{{country.twocode}}" maxlength="250" type="text" />
{%endfor%}
</div>
<input type="submit" class="btn btn-default" name="submit" value="Save">
</form>
Using this forms.py class:
class GeoBonusForm(forms.ModelForm):
class Meta:
model = GeoBonus
fields = ['bookmaker']
But request.POST does contain only bookmaker field.
EDIT1: Views.py
#staff_member_required
def geo_bonus_edit(request, bookmaker=None):
template = loader.get_template('geobonus/edit.html')
if request.method == 'POST':
form = GeoBonusForm(request.POST)
print request.POST
else:
form = GeoBonusForm()
context = RequestContext(request, {
'form': GeoBonusForm,
'countries': Country.objects.all(),
})
return HttpResponse(template.render(context))
I would suggest, that you generate the fields in your form dynamically. It could look like this:
class GeoBonusForm(forms.ModelForm):
countries = Country.objects.all()
def __init__(self, *args, **kwargs):
super(GeoBonusForm, self).__init__(*args, **kwargs)
for country in self.countries:
self.fields[country.name] = forms.CharField()
This allows you to generate a CharField for every Country you have.
Therefore the saving is a bit different than the normal ModelForm would expect it I would recommend to override the save method:
def save(self, commit=True):
super(GeoBonusForm, self).save(commit=False)
bookmaker = Bookmaker.objects.get(id=self.cleaned_data['bookmaker'].id)
for field in self.cleaned_data:
if field != 'bookmaker':
country = Country.objects.get(name=field)
geo_bonus, created = GeoBonus.objects.get_or_create(bookmaker=bookmaker, country=country)
geo_bonus.name = self.cleaned_data[field]
geo_bonus.save()
At first we try to get the chosen bookmaker. After that we iterate over the cleaned fields (for more about form cleaning take a look here) and try to get_or_create the GeoBonus object. Now we just fill in the value of the corresponding country field and save.
I adapted the code of your view a little bit:
def index(request, bookmaker=None):
template = loader.get_template('geobonus/edit.html')
if request.method == 'POST':
form = GeoBonusForm(request.POST)
if form.is_valid():
form.save()
else:
form = GeoBonusForm()
context = RequestContext(request, {
'form': GeoBonusForm,
})
return HttpResponse(template.render(context))
You don't need to pass the countries to the context anymore, because we generate the field in the form. On POST we check if the form is valid and save it, if it is.
For the template you only should only need this now:
<form action="." method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
Hope I got it right and that this solves your problem.
Edit: Note, that this is a simple quick example. Of course you should clean the data and check if the input is valid and maybe prepare it for a more simple saving process.
You could use Django's inlinemodelformset for that, refer to using a formset in views and templates for example code.
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
I have the following models:
class Profile(models.Model):
verified = models.BooleanField(default=False)
primary_phone = models.OneToOneField('Phone', related_name='is_primary', null=True, blank=True)
class Phone(models.Model):
profile = models.ForeignKey(Profile)
type = models.CharField(choices=PHONE_TYPES, max_length=16)
number = models.CharField(max_length=32)
#property
def is_primary(self):
return profile.primary_phone == self
And the following forms:
class PhoneForm(ModelForm):
class Meta:
from accounts.models import Phone
model = Phone
fields = ('type', 'number', )
which is being used in a modelformset_factory.
I'm rendering the formset like this:
<div class="span-13 last">
{{ formset.management_form }}
{% for form in Phones %}
<div class="span-2">{{ form.type|add_class:'dropdown' }}</div>
<div class="span-11 last">{{ form.number|add_class:'phone-number' }}</div>
<div class="clearfix"></div>
{% endfor %}
</div>
Now what I want to do is to render a radio button in the template to reflect the is_primary property of Phone model. There are two ways to determine this relationship, through Phone model itself or through Profile.primary_phone. But then I'm rendering Phone model as a formset, hence looping over its instances, so I tried to include 'is_primary' in PhoneForm fields, but it did not work since it's a property.
Any idea how to do this?
UPDATE #1:
I have used jpic approach and tried to render primary as radio buttons:
class PhoneForm(ModelForm):
primary = forms.BooleanField(widget=forms.RadioSelect( choices=((0, 'False'), (1, 'True')) ))
class Meta:
from accounts.models import Phone
model = Phone
fields = ('primary', 'type', 'number', )
However, it shows two radio buttons for each instance of Phone while I need it to show only one radio button per instance. I'm going to play around with it for a while and see if I can get it to show correctly.
Instead of:
class Profile(models.Model):
verified = models.BooleanField(default=False)
primary_phone = models.OneToOneField('Phone', related_name='is_primary', null=True, blank=True)
class Phone(models.Model):
profile = models.ForeignKey(Profile)
type = models.CharField(max_length=16)
number = models.CharField(max_length=32)
You should have:
class Profile(models.Model):
verified = models.BooleanField(default=False)
def primary_phone(self):
return self.phone_set.get(primary=True)
class Phone(models.Model):
profile = models.ForeignKey(Profile)
type = models.CharField(max_length=16)
number = models.CharField(max_length=32)
primary = models.BooleanField(default=False)
def save(self, force_insert=False, force_update=False, using=None):
if self.primary:
# clear the primary attribute of other phones of the related profile
self.profile.phone_set.update(primary=False)
self.save(force_insert, force_update, using)
That would make your life easier.
If you cannot make this change: a Phone formset is actually a wrapper around many Phone forms. But the field you're after allows to edit Profile.primary_phone.
So one way of doing it is to do it manually as such:
{% for form in Phones %}
<input type="radio" name="primary_phone" checked="{% if form.instance == profile.primary_phone %}checked{% endif %}" value="{{ form.instance.pk }}" />
<!-- snip ... ->
But the problem is that the radio won't have a value for empty Phone forms, as the value is {{ form.instance.pk }}.
Another way of doing it is to add a checkbox to PhoneForm:
from django import forms
from accounts.models import Phone
class PhoneForm(forms.ModelForm):
primary = forms.BooleanField(required=False, default=False)
class Meta:
model = Phone
fields = ('type', 'number', )
We're using a BooleanField here because for each Phone form, primary is to be set or not. But still, you'll have to render it yourself:
{% for form in Phones %}
<input type="checkbox" name="{{ form.prefix }}-primary" checked="{% if form.instance == profile.primary_phone %}checked{% endif %}" value="true" />
<!-- snip ... ->
But then, you need javascript to ensure only one radio is checked at the time, e.g. with jQuery:
$('input[type=checkbox]').change(function() {
$('input[type=checkbox][checked=checked]').attr('checked', '');
$(this).attr('checked', 'checked');
});
Of course, you should update the selectors in this example above to ensure only "primary phone" checkboxes are affected.
Finally, to connect the checkbox, something like this might work:
class PhoneForm(forms.ModelForm):
primary = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super(PhoneForm, self).__init__(*args, **kwargs)
if self.instance.is_primary:
self.data['primary'] = True
def save(self, *args, **kwargs):
super(PhoneForm, self).save(*args, **kwargs)
if self.cleaned_data['primary']:
self.profile.primary_phone = self
self.profile.save()