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()
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 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
Forgive the logic of the table structure below example. It only meant as a simple example of the situation that I am.
So the situation is that I want to make an employee form page, where the department and line manager might or might not exist already. So I replace the drop-down box with the form field for the foreign key, so they can be created if need all in one step for the user. However, with this kind of dependency, I am not doing the right thing in view to make it work.
If you need more detail please do ask.
If you can make the Title more precise please do.
Thank you for your time.
Model
class Employee(models.Model):
name = models.CharField()
age = models.CharField()
line_manager = models.ForeignKey(LineManager)
department = models.ForeignKey(Department)
class LineManager(models.Model):
manager_name = models.CharField()
department = models.ForeignKey(Department)
class Department(models.Model):
department_name = models.CharField()
Form
class EmployeeForm(ModelForm):
class Meta:
model = Employee
fields = ['name',
'age'
#'line_manager' Not needed
#'department' Not needed]
exclude = ('line_manager', 'department')
class LineManagerForm(ModelForm):
class Meta:
model = LineManager
fields = ['manager_name']
exclude = ('department')
# There is a basic form for Department, as well.
View
class AddEmployeeView(View):
forms = {'department': DepartmentForm(self.request.POST or None),
'line_manager': LineManagerForm(self.request.POST or None),
'employee': EmployeeForm(self.request.POST or None)]}
def get(self, request, *args, **kwargs):
form_list = [form for _,form in forms]
return render (request, 'app/temp.html', {'forms': form_list}
def post(self, request, *args, **kwargs):
if all([form.is_valid() for _,form in forms]):
department_data = forms['department'].cleaned_data
department_obj, _ = Department.objects.get_or_create(department_data)
line_manager_instance = forms['line_manager'].instance
line_manager_instance.department = department_obj
line_manager_data = forms['line_manager'].cleaned_data
line_manager_obj, _ = LineManager.objects.get_or_create(line_manager_data)
employee_instance = forms['employee'].save(commit=False)
employee_instance.department = department_obj
employee_instance.line_manager = line_manager_obj
employee_instance.save()
html
<form method="POST">
{% csrf_token %}
{% form in forms %}
{{ form.as_p }}
{% endform %}
<input type='submit'/>
</form>
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.
I have a fairly simple DetailView:
class TrackDetails(DetailView):
model = Track
And in my urls.py:
url(r'^(?P<slug>[-\w]+)/$', TrackDetails.as_view(), name='track-details'),
The model:
class Track(models.Model):
....
# Variables
track_type_choices = [
('ORG', 'Original'),
('RMX', 'Remix'),
('CLB', 'Collab'),
('LIV', 'Live'),
]
# Model Fields
user = models.ForeignKey(User, unique=False)
title = models.CharField(max_length=100)
desc = models.TextField(max_length=7500)
track_type = models.CharField(max_length=3,
choices=track_type_choices,
default='ORG')
track_type_content = models.CharField(max_length=100,blank=True)
created = models.TimeField(auto_now=True,auto_now_add=False)
upload = models.FileField(upload_to=generate_user_folder_tracks,storage=OverwriteStorage(),validators=[is_mp3])
albumart = models.ImageField(upload_to=generate_user_folder_art,storage=OverwriteStorage(),validators=[is_square_png])
private = models.BooleanField(default=False)
downloadable = models.BooleanField(default=False)
likes = models.ManyToManyField(User, related_name="likes",blank=True)
dislikes = models.ManyToManyField(User, related_name="dislikes",blank=True)
plays = models.BigIntegerField(default=0)
slug = models.SlugField(max_length=50,unique=True)
The model displayed in the view has a "user" field connected to the user model, I want to use this in the url, so that instead of writing "www.domain.com/slug/" I would write "www.domain.com/user/slug" to access the view of the instance.
Additionally, I have extended the User model with a field called "Display_name", I'd like to show this field instead of the username in my template (track_detail.html):
{% include '__header.html' %}
{% load static from staticfiles %}
<div id="track_container">
<div id="track_titleinfo">
<div id="track_artist" class="text">{{ object.user }}</div>
<div id="track_title" class="text">{{ object.title }}</div>
{% if object.track_type == 'ORG' %}
{% else %}
<div id="track_type" class="text">({{object.track_type_content}})</div>
{% endif %}
</div>
</div>
{% include '__footer.html' %}
<img src="/static/users/{{ object.user }}/art/{{ object.slug }}.png" alt="">
The div with the ID "track_artist" displays the raw username (In this case, enitoni), I'd like it to display the display_name (In this case "Ekchö") from the userprofile class of the user who owns the Track instance:
class UserProfile(models.Model):
user = models.OneToOneField(User)
display_name = models.CharField(max_length=50, default="null")
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
To include the username in the detail view, you first need to add it to your url patterns.
url(r'^(?P<username>[-\w]+)/(?P<slug>[-\w]+)/$', TrackDetails.as_view(), name='track-details'),
Then, since you are using DetailView, you need to override get_object so that you use the username and slug to fetch the object.
from django.shortcuts imporg get_object_or_404
class TrackDetails(DetailView):
model = Track
def get_object(self, queryset=None):
return get_object_or_404(
Track,
user__username=self.kwargs['username'],
slug=self.kwargs['slug'],,
)
Displaying the display_name of the user in the template is a separate problem. If you have a user, you can follow the one to one key backwards to the profile with user.userprofile. Therefore, in your template you can show the display_name with.
{{ object.user.userprofile.display_name }}
To access username and slug first pass in the two keywords:
url(r'^/(?P<username>\d+)/(?P<slug>[-\w]+)/$', get_userpage, name='track-details'),
Then check if Track.objects.filter(slug=slug, username=username) returns anything:
def get_userpage(request, username, slug):
"""Render user page"""
user = User.objects.get(username=username)
track_song = Track.objects.filter(slug=trackslug, user=user).first()
if track_song:
# return song page for user
else:
# try to return user
track_user = Track.objects.filter(user=user).first()
if track_user:
# return user page
# if nothing returned above
# return 404
Previous suggestions:
you can you use get_object_or_404(Track, slug=slug) in your view to return the correct response.
you could also redirect a user to their unique combination of username and slug using:
redirect('track-username-slug', username=myusername slug=myslug, permanent=True)
where track-username-slug is your named url