Passing two Models in update view to a same template - django

I want to render two models User (built in model) and Profile model to the same template profile_form.html so that the user can update the data of both User model as well as Profile model
This is my Profile model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.png', upload_to='profile_pics')
description = models.TextField()
def __str__(self):
return self.user.username + "'s Profile"
This is my profile_form.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block title %}
Make your Profile
{% endblock title %}
{% block content %}
<div class="container mb-6">
<form action="" method="POST" class="form-group">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>
{% endblock content %}
This is my UserUpdateView
class UserUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model=User
fields=['username', 'first_name', 'last_name']
success_url='/'
def test_func(self):
x = self.request.user.id
y = self.kwargs['pk']
if x == y:
return True
else:
if self.request.user.is_authenticated:
raise Http404("You are not authenticated to edit this profile")
I want my Profile model's to be below User model's form
Please help me with this

To add a OneToOne-relation into the same view, you just need to overwrite the get_context_data method and provide an additional form.
If you don't have a profile form yet, just create a simple one:
#yourapp/forms.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = "__all__"
Now, to use this in your updateview, import it and you will need to change it like this:
class UserUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model=User
fields=['username', 'first_name', 'last_name']
success_url='/'
# create context manually
def get_context_data(self, **kwargs):
data = super(UserUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
data["profile"] = ProfileForm(self.request.POST)
else:
# accessing the profile object
data["profile"] = ProfileForm(instance=self.object.profile)
return data
And now your template will have access to the context profile
...
{{ form|crispy }}
{{ profile|crispy }}
...

Related

Making a CreateForm with choices based on database values

I am making a django project and I have a form for the User to add a Vehicle Manually that will be assigned to him. I also would like to had an option for the user to choose a vehicle based on the entries already present in the database.
vehicles/models.py
class Vehicle(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
nickname = models.CharField(unique = True, max_length=150)
date_joined = models.DateTimeField(default=timezone.now)
brand = models.CharField(max_length=150)
battery = models.CharField(max_length=150)
model = models.CharField(max_length=150)
def __str__(self):
return self.nickname
def get_absolute_url(self):
return reverse('vehicle-list')
class Meta:
db_table = "vehicles"
I created a form so the user can add his Vehicles as such:
vehicles/forms.py
class VehicleAddFormManual(forms.ModelForm):
class Meta:
model = Vehicle
fields = ('brand','model', 'battery', 'nickname')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
self.fields['brand']
self.fields['model']
self.fields['battery']
self.fields['nickname']
The corresponding view:
vehicles/views.py
class AddVehicleViewManual(LoginRequiredMixin, CreateView):
model = Vehicle
form_class = VehicleAddFormManual
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
The html file:
vehicles/templates/vehicles/vehicle_form.html
{% extends "blog/base.html" %}
{% block content %}
{% load crispy_forms_tags %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Vehicle</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Submit</button>
</div>
</form>
</div>
{% endblock content %}
I would like to add another form in which the user has a dropdown with option with the brands, models and batteries that already exist in the database. If there's a car in the database with brand: Tesla, model: Model 3, battery: 50 kWh, then it would appear in the dropbox as a choice for each field.
I'm not sure how to do this and sorry for the newbie question... Thanks in advance!
I once had to do something similar, but I needed a form which had one checkbox for each item in a list of externally-supplied strings. I don't know if this is the cleanest way, but I used python metaclasses:
class SockSelectForm(forms.Form):
#staticmethod
def build(sock_names):
fields = {'sock_%s' % urllib.parse.quote(name):
forms.BooleanField(label=name, required=False)
for name in sock_names}
sub_class = type('DynamicSockSelectForm', (SockSelectForm,), fields)
return sub_class()
In my get() method, I instantiate it as:
form = SockSelectForm.build(names)
and the corresponding form handling in the post() method is:
form = SockSelectForm(request.POST)
I suspect if you look under the covers of Django's ModelForm, you'd see something similar, but I couldn't use ModelForm because it's too closely tied to the model system for what I needed to do.
model.py
class DropdownModel(models.Model):
brand = models.CharField(max_length=150)
battery = models.CharField(max_length=150)
model = models.CharField(max_length=150)
def __str__(self):
return self.brand.
form.py
from .models import DropdownModel
all_brand = DropdownModel.objects.values_list('brand','brand')
all_battery = DropdownModel.objects.values_list('battery','battery')
all_model= DropdownModel.objects.values_list('model','model')
class DropdownForm(forms.ModelForm):
class Meta:
model = DropdownModel
fields = "__all__"
widgets = {
'brand':forms.Select(choices=all_brand),
'battery':forms.Select(choices=all_battery),
'model':forms.Select(choices=all_model),
}
view.py
from django.shortcuts import render
from .form import DropdownForm
# Create your views here.
def HomeView(request):
form = DropdownForm()
context = {'form':form}
return render(request,'index.html',context)
index.html
{% extends "base.html" %}
{% load static %}
{% block title %}
Index | Page
{% endblock title %}
{% block body %}
{{form.as_p}}
{% endblock body %}
Output-
Note- if u can't see updated values in dropdown do server restart because localhost not suport auto update value fill in dropdown it's supoorted on live server
Thank you

Django How to pass variable from a view to a form view

I have two models. One is project and another is todo. The todo model has a foreign key that is the related project's id.
I have a template that displays the individual project and generates a link to a form to add a todo list. How do I pass the project id to the todo form?
I guess I could simply pass the project id in the URL but is that the best way?
My current views.py
class CompanyProjectsDetailView(DetailView):
model = Project
id = Project.objects.only('id')
template_name = 'company_accounts/project_detail.html'
class TodoCreateView(CreateView):
model = ProjectTodo
template_name = 'company_accounts/add_todo.html'
fields = ['title', 'notes', 'status']
Here is my template:
{% extends 'base.html' %}
{% block content %}
<h1>Add Todo</h1>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="save">
</form>
{% endblock content %}
To check to see if the project id is being passed to the form I have tried {{ project.id }} in the template and several other things that have not worked.
Adding this to the views solved the issue:
class TodoCreateView(CreateView):
model = ProjectTodo
template_name = 'company_accounts/add_todo.html'
fields = ['title', 'notes', 'status']
def form_valid(self, form):
project = get_object_or_404(Project, id=self.kwargs.get('pk'))
todo = form.save(commit=False)
todo.project = project
todo.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('project_detail', args=[self.kwargs.get('pk')])

queryset when building django form

I am trying to get specific querysets based when a customer-specific form loads, showing only that customer's name (embedded as an ID field), its respective locations and users.
The idea is to select one user and any number of locations from a multichoice box.
I've tried to pass the ID as a kwarg but am getting a KeyError. I've tried the kwarg.pop('id') as found on the web and same issue. Any advice?
forms.py
class LocGroupForm(forms.ModelForm):
class Meta:
model = LocationsGroup
fields = ('group_name', 'slug', 'customer', 'location', 'user_id',)
def __init__(self, *args, **kwargs):
qs = kwargs.pop('id')
super(LocGroupForm, self).__init__(*args, **kwargs)
self.fields['customer'].queryset = Customers.objects.get(pk=qs)
self.fields['location'].queryset = CustomerLocations.objects.filter(customer_id=qs)
self.fields['user_id'].queryset = CustomerUsers.objects.filter(customer_id=qs)
here is my view. it's just a generic view
views.py
class LocGroupCreate(LoginRequiredMixin, CreateView):
form_class = LocGroupForm
template_name = 'ohnet/a_gen_form.html'
the template is a dry template I use for all my forms- admittedly something I mostly stole from a tutorial
{% extends "ohnet/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% load static %}
<div class="container">
<h1>{{ title }}</h1>
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" name="submit" value="Submit">
</form>
</div>
{% endblock content %}
This is the KeyError from the form load.
You need to pass a value for the id when constructing the LocGroupForm, you can do that by overriding get_form_kwargs:
class LocGroupCreate(LoginRequiredMixin, CreateView):
form_class = LocGroupForm
template_name = 'ohnet/a_gen_form.html'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['id'] = …
return kwargs
You will need to fill in the … that specifies the value passed as id to the form. This might for example be self.request.user.pk, or a URL parameter with self.kwargs['name-of-url-parameter']

django - image isn't saving, all other fields are

When I update the user profile via the view everything is saving to the db except the image. The forms are validating but image isn't being saved. I can log in the admin portal and successfully add an image to an existing instance. I assume my problem lies in my html template but I can't figure out what it is.
**Btw I've read multiple similiar post but none I believe addresses my issue.
form.py
class EditUserForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
)
class EditProfileForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = UserProfile
fields = (
'description',
'city',
'website',
'phone',
'image',
)
views.py
#transaction.atomic
def edit_profile(request):
if request.method == 'POST':
form = EditUserForm(request.POST, instance=request.user)
form2 = EditProfileForm(request.POST, instance=request.user.userprofile)
if form.is_valid() and form2.is_valid():
form.save()
form2.save()
return redirect(reverse('accounts:view_profile'))
else:
form = EditUserForm(instance=request.user)
form2 = EditProfileForm(instance=request.user.userprofile)
args = {'form': form, 'form2':form2}
return render(request, 'accounts/edit_profile.html', args)
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
edit_profile.html
<div class="container">
{% if form.errors %}
<ol>
{% for field in form %}
<H3 class="title">
<p class="error"> {% if field.errors %}<li>{{ field.errors|striptags }}</li>{% endif %}</p>
</H3>
{% endfor %}
</ol>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form2.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>
If you are uploading files, you must instantiate the form with request.POST and request.FILES.
form2 = EditProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
See the docs on file uploads for more info.

Setting a parents value in a childs object's model form in Django?

I wish for one of a parent's variables to be pre-populated in a child's model form specifically a serial number. I have managed to get the serial number as part of the URL but would like to figure out how it can be implemented as a variable on the form page.
Models.py
class Product(models.Model):
serial_number = models.CharField(unique=True, max_length=15)
class ProductInstance(models.Model):
serial_number = models.ForeignKey('Product', on_delete=models.SET_NULL, null=True)
Views.py
class ProductInstanceCreate(CreateView):
model = ProductInstance
template_name = 'myapp/edit_productinstance.html'
form_class = GunInstanceForm
def get_success_url(self):
return reverse_lazy ('product-detail', kwargs={'pk': self.object.serial_number.pk})
Forms.py
class ProductInstanceForm(forms.ModelForm):
class Meta:
model = ProductInstance
fields = '__all__'
templates/myapp/product_detail.html
...
New
...
urls.py
urlpatterns += [
url(r'^productinstance/(?P<serial_number>[-\w]+)/create/$', views.ProductInstanceCreate.as_view(), name='productinstance_create'),]
templates/myapp/edit_productinstance_form.html
{% extends "base_generic.html" %}
{% block content %}
<h2>Serial Number: {{ serial_number }}</h2>
</br>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Submit" />
</form>
</br>
Back
{% endblock %}
So currently I can create a URL such as: productinstance/D1430913/create/
I now need to know:
How to use it as a variable for the title?
How to set the the forms default value to it?
For the title:
<h2>Serial Number: {{ form.serial_number.value }}</h2>
I believe if you modify your CreateView like so and implement the serial_number_func() with whatever you need to get the serial number, this will do what you want:
class ProductInstanceCreate(CreateView):
model = ProductInstance
template_name = 'myapp/edit_productinstance.html'
form_class = GunInstanceForm
def get_form_kwargs(self):
kwargs = super(ProductInstanceCreate, self).get_form_kwargs()
kwargs['serial_number'] = serial_number_func()
return kwargs