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']
Related
I use these two classes to get the context data and check permissions respectively:
class RestaurantView(View):
def get_context_data(self):
ctx = {'restaurant': get_object_or_404(Restaurant, slug=self.kwargs['slug'])}
return ctx
class ManageRestaurantMixin(LoginRequiredMixin, UserPassesTestMixin, RestaurantView):
def test_func(self):
ctx = super().get_context_data()
return ctx['restaurant'].owner == self.request.user
Now the first one is working, so when i don't need permission i get the expected behavior, for example with this DetailView:
class Info(RestaurantView, DetailView):
model = Restaurant
context_object_name = 'restaurant'
template_name = 'restaurant/info.html'
But then if inherit from ManageRestaurantMixin the permissions are checked as expected, but the context object is not working and the template displays an empty form:
class EditInfo(ManageRestaurantMixin, UpdateView):
model = Restaurant
context_object_name = 'restaurant'
template_name = 'restaurant/edit_info.html'
fields = ['name', 'address', 'logo']
success_url = '/account/myrestaurants'
I get that the context gets overwritten, but i don't get how.
How can i solve this?
Is there a better way to handle this kind of situations?
Edit:
edit_info.html:
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<input class="btn btn-primary" type="submit" value="update">
</form>
{% endblock %}
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')])
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 }}
...
I need a model with some FloatField feilds, each records of this table should be used with another one (first row indicates minimum and the second one determines maximum).
I like the values of these two rows be added or edited simultaneously by user.
I'm eager to know how to use django CreateView and UpdateView generic classes or any other tools for this purpose.
Please let me know your helpful comments.
Update:
this is a part of my codes:
#models.py
class DefinedInfo(models.Model):
user = models.CharField(max_length=30)
bound = models.CharField(max_length=3) #takes 'min' or 'max'
density = models.FloatField(default=0)
weight =models.FloatField(default=0)
....
#views.py
class InfoCreateView(LoginRequiredMixin, CreateView):
model = DefinedInfo
template_name = 'item_new.html'
fields = ['density', 'weight' ...]
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.bound = 'min'
return super().form_valid(form)
class InfoUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = DefinedInfo
fields = ['density', 'weight' ...]
template_name = 'item_edit.html'
login_url = 'login'
def test_func(self):
obj = self.get_object()
return obj.user == self.request.user
#Html template
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div>
<h5>Define new item</h5>
<div>
<form action="" method="post">{% csrf_token %}
{{ form|crispy }}
<button" type="submit">Save </button>
<button type="reset" onclick="location.href={% url 'profile' %}" >Cancel</button>
</form>
</div>
</div>
{% endblock %}
There is no problem to add or edit individual records, but I prefer to handle "min" and "max" related records at the same time.
Generic Class based Views are made to update/create/delet one single object of a Model at a time.
If you want to make some customizations, it might be clearer and easier to write your own views.
Making a UpdateModel(UpdateView) is confusing if it does modify two rows in the DB.
I am building a view that will let me update multiple fields on multiple objects at the same time. I'm doing this using ModelFormSet & modelformset_factory.
The template will be a table of forms with the object name to the left of the fields (see image below).
I found this example, but I am stuck on how to implement the class based view & template.
My Formset
class BaseFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseFormSet, self).__init__(*args, **kwargs)
self.queryset = Reference.objects.filter(
start__isnull=True)
ReferenceFormSet = modelformset_factory(
Reference,
fields=('start', 'end'),
formset=BaseFormSet,
extra=0)
My View
class ReferenceFormSetView(LoginRequiredMixin, SuperuserRequiredMixin, FormView):
model = Reference
form_class = ReferenceFormSet
template_name = "references/references_form.html"
def form_valid(self, form):
for sub_form in form:
if sub_form.has_changed():
sub_form.save()
return super(ReferenceFormSetView, self).form_valid(form)
My Template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h1>{{ headline }}</h1>
<div class="row">
<form action="" method="post">
{% crispy form %}
<div class="">
<input type="submit" value="Submit" />
</div>
</form>
</div>
</div>
{% endblock content %}
Questions
The view seems odd with the Formset in the form_class. Is there a better way to handle this?
How can I access the instance name to display in the form?
I found a solution using a package called django-extra-views.
There is a class called ModelFormSetView which does exactly what I wanted. Here is my implementation (simplified) for others to use -
My View
class ReferenceFormSetView(ModelFormSetView):
model = Reference
template_name = "references/references_form.html"
fields = ['start', 'end']
extra = 0
def get_queryset(self):
return self.model.objects.all()
def get_success_url(self):
return reverse('references:formset')
def formset_valid(self, formset):
"""
If the formset is valid redirect to the supplied URL
"""
messages.success(self.request, "Updated")
return HttpResponseRedirect(self.get_success_url())
def formset_invalid(self, formset):
"""
If the formset is invalid, re-render the context data with the
data-filled formset and errors.
"""
messages.error(self.request, "Error dummy")
return self.render_to_response(self.get_context_data(formset=formset))
My Template
<form class="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="">
{% for field in form %}
{{ field }}
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Save</button>
</form>