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 %}
Related
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 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']
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 }}
...
In part_detail_view I am passing part_stock_form() to the template so that I will show the form to add stock in the DetailView.
class part_detail_view(DetailView):
model = part_list
context_object_name = 'part_detail'
template_name = 'part_detail.html'
def get_context_data(self, **kwargs):
context = super(part_detail_view, self).get_context_data(**kwargs)
context['my_list'] = populate_nav_bar()
context['form'] = part_stock_form()
return context
The CreateView of stock is given below
class stock_add_view(CreateView):
model = part_stock
fields = ['part_id','entry_date','supplier','amount','remaining']
success_url = reverse_lazy('parts:part_list')
and the template of 'part_detail.html' is
<div>
{{ part_detail.part_id}}<br>
{{ part_detail.part_name }}<br>
{{ part_detail.cost }}<br>
{{ part_detail.available_quantity }}
</div>
<div >
<form method="post" action="{% url 'parts:stock_add_view'%}">
{% csrf_token %}
{{ form.errors }}
{{ form }}
<input type="submit">
</form>
</div>
when adding the stock through stock_add_view() the stock is added successfully but when invalid input is entered the error is shown like this
CreateView assumes that you have a template in the form of model_name_form. In your case, it is part_stock_form.html. From the docs:
The CreateView page displayed to a GET request uses a template_name_suffix of '_form'. For example, changing this attribute to '_create_form' for a view creating objects for the example Author model would cause the default template_name to be 'myapp/author_create_form.html'
So, you need to create such a template with the name part_stock_form.html. If you want to use part_detail.html, try changing template_name variable in your view (untested):
class stock_add_view(CreateView):
model = part_stock
fields = ['part_id','entry_date','supplier','amount','remaining']
success_url = reverse_lazy('parts:part_list')
template_name = 'part_detail.html'
Hope it helps
I'm working with a CreateView where I know what some of the field values will be ahead of time. In the example below, I know that the author field for a new Entry object will be the current user and I use get_initial() to preset this.
Now I would like to omit this field from my template form. I've tried several approaches:
Simply commenting out the author field in the form template leads to an invalid form.
Leaving 'author' out of fields. Nope.
And here's a related problem. The example below involves a relationship to a User object that exists. But what if I need to create an object, say an auth Group for editors? I've tried creating a placeholder group and renaming it ... and, well, that didn't work very well.
#
# model
#
class Author(Model):
name = CharField(max_length = 60)
user = OneToOneField(to = User, related_name = 'author_user', on_delete = CASCADE)
class Entry(Model):
title = CharField(max_length = 60)
author = ForeignKey(to = Author, related_name = 'entry_author')
#
# view
#
class EntryCreateView(CreateView):
model = Entry
fields = ('title', 'author')
def get_initial(self):
initial = super(EntryCreateView, self).get_initial()
initial['author'] = get_object_or_404(Author, user = self.request.user)
return initial
#
# template
#
{% extends "base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<label for="{{ form.title.id_for_label }}">Title:</label>
{{ form.title }}
<label for="{{ form.author.id_for_label }}">Author:</label>
{{ form.author }}
<p>
<input type="submit" class="btn btn-primary" name="save" value="Save" />
<input type="submit" class="btn btn-primary" name="cancel" value="Cancel" />
</form>
{% endblock %}
You can manually set user in form_valid() method of EntryCreateView class:
class EntryCreateView(CreateView):
model = Entry
fields = ('title',)
def form_valid(self, form):
user = self.request.user
form.instance.user = user
return super(EntryCreateView, self).form_valid(form)
You'll need to create a ModelForm for the customizations you need (https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/).
You can't remove author because it's required on your model currently.
Try something like this:
In forms.py...
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['title', 'author']
def __init__(self, *args, **kwargs):
initial = kwargs.get('initial', {})
self.author = initial.get('author')
super(EntryForm, self).__init__(*args, **kwargs)
You can make modifications to the fields (set to not required, delete a field from the form fields, etc) in __init__ or on the class.
Just import and reference this form in your views to use it.