I think I have a pretty basic UpdateView but the object is not saved when I submit the form. The success_url is never called. When I click the Update button, the form refreshes and I stay on the same page. I am able to update the object via admin, so I believe the model is working fine. I am not getting any errors.
urls
path('classroomdetail/<uuid:classroom_id>/',
views.classroomdetail, name='classroomdetail'),
path('classedit/<uuid:pk>/', views.ClassroomUpdateView.as_view(), name='classupdate'),
Model
class Classroom(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
classroom_name = models.CharField(max_length=20)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
students = models.ManyToManyField(Student)
class Meta:
constraints = [models.UniqueConstraint(
fields=['user', 'classroom_name'], name="unique-user-classroom")]
def __str__(self):
return self.classroom_name
views
class ClassroomUpdateView(UpdateView):
model = Classroom
fields = ['classroom_name']
template_name_suffix = '_update'
success_url = reverse_lazy('gradebook:classroom')
template
{% extends 'base.html' %} {% load static %} {% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row">
<div class="col">
<h3>This Classroom belongs to {{ classroom.course }}</h3>
</div>
</div>
<div class="row">
<div class="col-md-3">
<form class="form-group">
{% csrf_token %}{{ form|crispy }}
<input type="submit" class="btn btn-primary mt-2 mb-2" value="Update">
</form>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="ps-2">Cancel
</div>
</div>
</div>
{% endblock content %}
I was missing method="post" in my template. Correct line:
<form method="post" class="form-group">
Related
I am trying to create an educational website using Django, so when I am trying to render {{ profile.institution }} or {{ profile.grade }} or {{ profile.user.username }} they are not being rendered.I don't know why they aren't. Can anyone help me solve this?
My models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
institution = models.CharField(max_length = 100)
grade = models.CharField(max_length=100, choices= YEAR_IN_SCHOOL_CHOICES)
bio = models.TextField(max_length=300)
def __str__(self):
return f'{self.user.username} Profile'
My views.py:
class User_Profile(LoginRequiredMixin, ListView):
model = Profile
template_name = 'class/profile.html'
context_object_name = 'profile'
def get_queryset(self):
return Profile.objects.filter(user=self.request.user)
My html:
{% extends "class/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<br>
<div class="row d-flex justify-content-center">
<h1 style="color: #f5a425">Hello {{ user.username }}</h1>
</div>
<div class="container mt-5">
<div class="row d-flex justify-content-center">
<div class="col-md-7">
<div class="card p-3 py-4">
<div class="text-center">
<i class='fas fa-user-alt' style='font-size:36px'></i>
<!-- <img src="" width="100" class="rounded-circle"> -->
</div>
<div class="text-center mt-3">
<span class="bg-secondary p-1 px-4 rounded text-white">Pro</span>
<h5 class="mt-2 mb-0">{{ profile.user.username }}</h5>
<span>{{ profile.institution }}</span>
<span>{{ profile.grade }} Grade</span>
<div class="px-4 mt-1">
<p class="fonts">{{ profile.bio }}</p>
</div>
<div class="buttons">
<button class="btn btn-outline-primary px-4">Message</button>
<button class="btn btn-primary px-4 ms-3">Contact</button>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
what part of the code should I change to make this work ?
ListView it is about many objects ant iteration through the object_list. In this case the answer of #PouyaEsmaeili is correct.
But. The mistake is - you have a wrong view. DetailView is the right choose.
This returns always one object or nothing.
Profile.objects.filter(user=self.request.user)
In your case:
class User_Profile(LoginRequiredMixin, DetailView):
model = Profile
template_name = 'class/profile.html'
context_object_name = 'profile'
def get_object(self):
return get_object_or_404(self.model, user=self.request.user)
If you set name for template profile_detail.html, you don't need template_name attribute. It should be find automatically.
If you don't change the model_name in Profile._meta - , you don't need context_object_name attribute. It should be defined automatically.
Please, don't forget about Django-views naming best practices.
Last version of your view can be:
class ProfileDetailView(LoginRequiredMixin, DetailView):
model = Profile
def get_object(self):
return get_object_or_404(self.model, user=self.request.user)
How I can show selectbox from models in my form. Output for below code does not show options for selectbox.
#models.py
class Reg(models.Model):
options = (
('one', 'option1'),
('two', 'option2'),
)
types = models.CharField(max_length=30, choices=options,null=True)
company = models.CharField(max_length=50,null=True)
#form.py
from django import forms
from .models import Reg
class Regform(forms.ModelForm):
class Meta:
model = Reg
fields = ['types','company']
types = forms.CharField(
widget=forms.Select(
attrs={'class': 'form-control'}
),
label="Type",
)
templatefie.html
{% load static %}
{% block main %}
<form method="post" class="post-form" autocomplete="off">
{% csrf_token %}
<div class="row col-md-4 m-auto">
<div>
{{people.as_p}}
<button type="submit" class="save btn btn-primary mt-5">apply</button>
</div>
</div>
</form>
{% endblock %}
Labels and widgets can be defined separately.
Class RegForm(forms.ModelForm):
class Meta:
model = Reg
fields = ['types','company']
labels = {
'types': 'Types',
'company': 'Company'
}
widgets = {
'types': Select(attrs={'class':'form-control'})
}
Create a view function that creates instance of the "Reg" modelform and pass in context to the template context = {'form_obj':Regform()}
{% load static %}
{% block main %}
<form method="post" class="post-form" autocomplete="off">
{% csrf_token %}
<div class="row col-md-4 m-auto">
<div>
{{form_obj.types.label_tag}}
{{form_obj.types}}
<button type="submit" class="save btn btn-primary">apply</button>
</div>
</div>
</form>
{% endblock %}
There is some problem, I'm trying to update the product on the client by making changes and clicking on the update button - my page is refreshing w/o updating info, so the product has the same data as before. But in the logs, the status code of the GET request is 200 and shows the updated object in the database. When I try to update through the admin Django dashboard, everything works successfully, the problem is only on the client side of the web application. What issues can there be?
Thank you in advance!
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
CATEGORY = (
('Stationary', 'Stationary'),
('Electronics', 'Electronics'),
('Food', 'Food'),
)
class Product(models.Model):
name = models.CharField(max_length=100, null=True)
quantity = models.PositiveIntegerField(null=True)
category = models.CharField(max_length=50, choices=CATEGORY, null=True)
def __str__(self):
return f'{self.name}'
class Order(models.Model):
name = models.ForeignKey(Product, on_delete=models.CASCADE, null=True)
customer = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
order_quantity = models.PositiveIntegerField(null=True)
def __str__(self):
return f'{self.customer}-{self.name}'
views.py
#login_required(login_url='user-login')
#allowed_users(allowed_roles=['Admin'])
def product_edit(request, pk):
item = Product.objects.get(id=pk)
if request.method == 'POST':
form = ProductForm(request.POST, instance=item)
if form.is_valid():
form.save()
return redirect('dashboard-products')
else:
form = ProductForm(instance=item)
context = {
'form': form,
}
return render(request, 'dashboard/products_edit.html', context)
forms.py
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
html template:
{% extends 'partials/base.html' %}
{% block title %}Products Edit Page{% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row my-4">
<div class="col-md-6 offset-md-3 p-3 bg-white">
<h3>Edit Item</h3>
<hr>
<form>
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-info" type="submit" value="Confirm">
</form>
</div>
</div>
{% endblock %}
You have forgotten to pass POST method you are using GET.
{% extends 'partials/base.html' %}
{% block title %}Products Edit Page{% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row my-4">
<div class="col-md-6 offset-md-3 p-3 bg-white">
<h3>Edit Item</h3>
<hr>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-info" type="submit" value="Confirm">
</form>
</div>
</div>
{% endblock %}
Here are my forms:
class RoleForm(forms.ModelForm):
class Meta:
model = models.RoleModel
fields = ['name']
class FeatureForm(forms.ModelForm):
role = forms.ModelChoiceField(queryset=models.RoleModel.objects.values_list('name', flat=True).distinct())
class Meta:
model = models.FeatureModel
fields = ['role','feature']
In my bootstrap form, the choices display properly. I get a list of all roles. But if I fill the feature form and hit submit it says - "Select a valid choice. That choice is not one of the available choices."
My models are:
class RoleModel(models.Model):
name = models.CharField(validators=[alphanumeric], max_length=50, unique=True, blank=False)
class FeatureModel(models.Model):
role = models.ForeignKey(RoleModel, on_delete=models.PROTECT)
feature = models.CharField(validators=[alphanumeric], max_length=10, unique=True)
my bootsrtap form is:
<form action="{% url 'feature_form' %}" novalidate method="POST">{% csrf_token %}
<div class="row">
<div class="col">
<label for="role">{{ fform.role.label }}</label>
<p><select class="form-control id="role" name="role">
{% for item in fform.role %}
{{ item }}
{% endfor %}
</select></p>
{% for error in fform.role.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
<div class="col">
<label for="feature">{{ fform.feature.label }</label>
<p><input type="text" class="form-control" id="feature" name="feature"
{% if fform.feature.value is not None %}
value="{{ fform.feature.value }}"
{% endif %}
></p>
{% for error in fform.feature.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
</div>
<input class='btn btn-primary btn-sm' type='submit' value='Save'>
</form>
My need is simple. The second form (FeatureForm) has two fields. role being foreign key of another model and a text field to type in name of a feature. On my client side, I need the foreign key to be displayed as a select option with a list. I chose a value from this select, enter the value of feature and hit save. It has to save.
It doesn't work, because your queryset includes only names, but you need an id / primary key of a RoleModel. Since your choices don't have an id, they aren't a valid choice.
Firstly, your RoleModel name is set to unique and is therefore no point in querying distinct() name values because they will be distinct already by unique definition.
You also don't need to construct your own select input. Django will take care of this.
All you need is:
class RoleModel(models.Model):
name = models.CharField(validators=[alphanumeric], max_length=50, unique=True, blank=False)
def __str__(self):
return self.name
class FeatureForm(forms.ModelForm):
class Meta:
model = models.FeatureModel
fields = ['role', 'feature']
def __init__(self, *args, **kwargs):
super(FeatureForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs = {'class': 'form-control'}
and
<form action="{% url 'feature_form' %}" novalidate method="POST">
{% csrf_token %}
<div class="row">
<div class="col">
<label for="role">{{ fform.role.label }}</label>
<p>{{ fform.role }} </p>
{% for error in fform.role.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
<div class="col">
<label for="feature">{{ fform.feature.label }</label>
<p>{{ fform.feature }}</p>
{% for error in fform.feature.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
</div>
<input class='btn btn-primary btn-sm' type='submit' value='Save'>
</form>
EDIT: Otherwise, a Select input in this case should be constructed as:
<select>
<option value="1">Name 1</option>
<option value="2">Name 2</option>
</select>
where value is an id, a primary key of RoleModel in your case. Your select options don't have this value.
I'm creating a summary page of all the posts that the user has created and also favourited. However, it throws the the error above when trying to retrieve the users uploaded posts and I don't know why?
Models
class Aircraft(AircraftModelBase):
user = models.ForeignKey(User)
manufacturer = SortableForeignKey(Manufacturer)
aircraft_type = SortableForeignKey(AircraftType)
body = SortableForeignKey(Body)
engines = models.PositiveSmallIntegerField(default=1)
View
def account_overview(request):
fav_aircraft = FavoritedAircraft.objects.filter(user__id=request.user.id)
uploaded_aircraft = Aircraft.objects.filter(user=user) <---- HERE!!!!
fav_airline = FavoritedAirline.objects.filter(user__id=request.user.id)
return render(request, 'account/account_overview.html', {'favAircraft':fav_aircraft,
'favAirline':fav_airline,
'UploadedAircraft':uploaded_aircraft})
Template
{% if UploadedAircraft %}
<div class="col-md-12">
<i><h1><strong>Your Aircraft Uploads..</strong></h1></i>
{% for aircraft in UploadedAircraft %}
<div class="col-lg-offset-0 col-md-4 col-sm-3 item">
<div class="box"><img src="{{ aircraft.aircraft.image.url }}" width="200px" height="200px" alt="{{ aircraft.aircraft.title }}"/></a>
<h3 class="name">{{ aircraft.aircraft.name }}</h3>
<h4><em>Range: {{ aircraft.aircraft.maximum_range }}</em></h4>
<button class="btn btn-default" type="button">Edit </button>
<button class="btn btn-default" type="button">Delete </button>
</div>
{% endfor %}
</div>
</div>
{% else %}
<h2 class="text-center">Opps.. You don't seem to have any uploads..</h2></div>
{% endif %}
#your user is object of User model. Aircraft has foreign key from User model
#Try this code
uploaded_aircraft = Aircraft.objects.filter(user=user)