How to use ModelChoiceField in DRF? - django

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

Related

placeholder on dependent dropdown filter

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:])

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

Displaying get_context_data in template Django

I am trying to display the get_context_data on the template. I have a method on the model class that I need to call from ProfileView which has two different models. For the Profile View I have Profile Model and for the shippingaddress view I have ShippingAddress Model. And these models are from two different app. I tried the function below and it does not show any error, but when I tried to call it in the Template, It does not show the method.
Views.py
class ProfileView(LoginRequiredMixin, DetailView):
model = Profile
template_name = "account/profile.html"
success_url = "/"
def get_context_data(self, *args, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
context['shipping'] = ShippingAddress.objects.filter(user=self.request.user)
return context
Template code
{{object.get_full_address}}
Models.py
class ShippingAddress(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
phone_number = PhoneNumberField(null=True, blank=True)
street_address = models.CharField(max_length=300)
province = models.CharField(max_length=300)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
zip_code = models.CharField(max_length=10)
def __str__(self):
return str(self.user)
def get_phone_number(self):
return self.phone_number
#property
def get_full_address(self):
return f"{self.street_address}, {self.province}, {self.city}, {self.country}, {self.zip_code}"
object is the context variable that DetailView will add to the context. For your view this would be an instance of Profile. You pass a queryset into the context with the name shipping so you can loop over that:
{% for shipping_address in shipping %}
{{ shipping_address.get_full_address }}
{% endfor %}
Note: You need to loop because one user has multiple Shipping Addresses according to your models.
Note: Also you didn't need to override get_context_data you could simply have written:
{% for shipping_address in request.user.shippingaddress_set %}
{{ shipping_address.get_full_address }}
{% endfor %}
Where shippingaddress_set is the related model name with _set
appended. You can change that by setting related_name on your
foreign key.

Django: How to add a foreignkey to a dependent form, to a multi-form submit?

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>

How to let user choose which table needs to be updated Django

I have a form in my website, which is the same for three tables (Homework, Class, Random)
So basically I want to make a ChoiceField on the top of the form, to let user choose where to upload file.
I was thinking, because these tables have common abstract class, may be I can choose it from there somehow. But can not figure out how.
Or may be there is much better solution for this.
just in case this is my code:
#models.py
class FileDescription(models.Model):
class Meta:
abstract = True;
ordering = ['file_creation_time']
subject = models.ForeignKey('Subjects', null=True, blank=True, primary_key=True)
subject_name = models.CharField(max_length=100)
file_uploaded_by = models.CharField(max_length=100)
file_name = models.CharField(max_length=100)
file_description = models.TextField()
file_creation_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return u'%s' % (self.file_name)
#template
<ul id="tabs">
<li>Homework</li>
<li>Class Papers</li>
<li>Random Papers</li>
</ul>
<div id="homework" class="tab-section">
<h2>Homework</h2>
<p>This section contains Homework</p>
{% if homework_files %}
<ul>
{% for file in homework_files %}
<li>{{ file.file_name }}
{% endfor %}
</ul>
{% endif %}
</div>
#forms.py
class Homework_Content_Form(forms.ModelForm):
class Meta:
model=Homework_Content
exclude=('subject',
'subject_name',
'file_creation_time',
'file_uploaded_by',
)
Method 1: Separate Forms
class GenericContentForm(forms.ModelForm):
class Meta:
exclude = ('subject', 'subject_name', 'file_creation_time', 'file_uploaded_by')
TYPE_CHOICES = (
('homework', 'Homework'),
('class', 'Class Paper'),
('random', 'Random Paper'),
)
type = forms.ChoiceField(choices=TYPE_CHOICES)
class HomeworkForm(GenericContentForm):
class Meta(GenericContentForm.Meta):
model = Homework
class ClassPaperForm(GenericContentForm):
class Meta(GenericContentForm.Meta):
model = ClassPaper
class RandomPaperForm(GenericContentForm):
class Meta(GenericContentForm.Meta):
model = RandomPaper
Then in your view you just pick one to start with, and when you have the POST data, you can instantiate a different one instead:
def my_view(request):
if request.method == 'POST':
type = request.POST.get('type')
if type == 'homework':
form = HomeworkForm(request.POST)
elif type == 'class':
form = ClassPaperForm(request.POST)
elif type == 'random':
form = RandomPaperForm(request.POST)
else:
form = HomeworkForm()
...
Method 2: Use Proxy Models
Since these three models all share the same data, having three separate tables is redundant. Instead of FileDescription being abstract, make it just a normal standard model, and add a field to it for type, with choices of "Homework", "Class Paper" and "Random Paper". Then create proxy models for each:
class HomeworkManager(models.Manager):
def get_query_set(self, *args, **kwargs):
qs = super(HomeworkManager, self).get_query_set(*args, **kwargs)
return qs.filter(type='homework')
class Homework(FileDescription):
class Meta:
proxy = True
objects = HomeworkManager()
def save(self, *args, **kwargs):
self.type = 'homework'
super(Homework, self).save(*args, **kwargs)
Then, you just need one form for FileDescription and when the user's choice for the "type" will be saved. You can then access anything set as type "homework" with the standard Homework.objects.all().