this is my first Django project and I am stuck. If not a solution, but just a hint on what I am doing wrong will be truly appreciated.
I have a model with one attribute set as Null by default and I want to use a Form to update that attribute with the ID taken from another model.
These are the 2 models:
models.py
class Squad(models.Model):
squad_name = models.CharField(max_length=20)
def __str__(self):
return self.squad_name
class AvailablePlayer(models.Model):
player_name = models.CharField(max_length=25)
squad = models.ForeignKey(Squad, on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.player_name
This is the form:
forms.py
class AddSquadToPlayerForm(forms.Form):
# squad the player will be added to
squad_to_player = forms.ModelMultipleChoiceField(queryset=None)
def __init__(self, *args, **kwargs):
super(AddSquadToPlayerForm, self).__init__()
self.fields['squad_to_player'].queryset = AvailablePlayer.objects.all()
This is the view file, where I think something is missing/wrong:
views.py
def add_player_to_squad(request, squad_id):
# player = AvailablePlayer.objects.get(id=squad_id)
squad = Squad.objects.get(id=squad_id)
if request.method == "POST":
form = AddPlayerToSquadForm(request.POST)
if form.is_valid():
form.squad = form.cleaned_data['id']
form.save()
return redirect('fanta_soccer_app:squad', squad_id=squad_id)
else:
form = AddPlayerToSquadForm(queryset=AvailablePlayer.objects.all())
context = {"squad": squad, "form": form}
return render(request, 'fanta_soccer_app/add_player.html', context)
And finally, this is html file
add_player.html
<body>
<p>Add a new player to the Squad:</p>
<p>{{ squad }}</p>
<form action="{% url 'fanta_soccer_app:add_player' squad.id %}" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<button name="submit" type="submit" >Add player</button>
</form>
</body>
The rendered html shows correctly a form with a drop down menu correctly populated with the objects in the database from the "AvailablePlayer" model, however when selecting one and clicking on "Add", nothing happens, and selected player is not added to the DB.
Thank you in advance for your time.
EDIT: the code in views.py has been modified according to a comment
ADDITIONAL INFO: to confirm the db is working, if I manually add the squad_id to one of the AvailablePlayer(s) in the DB, it will be correctly listed in the squad details view.
Based off the docs I think where you are going wrong is that you are trying to update an existing value, which requires you to pass an instance keyword argument to form.save().
So I think you need to do something like this:
if form.is_valid():
a = Squad.objects.get(id=squad_id)
form = AddSquadToPlayerForm(request.POST, instance=a)
form.squad = form.cleaned_data['id']
form.save()
I managed to fix it by myself, and sharing here the solution:
I changed the form field from ModelMultipleChoiceField to ModelChoiceField. ModelMultipleChoiceField is for ManyToMany relationships model fields, while mine is a ForeignKey. I have also removed the init method:
squad_to_player = forms.ModelChoiceField(queryset=AvailablePlayer.objects.all())
The view was pretty messed up, as I was aware of, but after fixing the above, I started to have error logs, so I could eventually come up with the following solution:
view.py
def add_player_to_squad(request, squad_id):
squad = Squad.objects.get(id=squad_id)
if request.method == "POST":
form = AddPlayerToSquadForm(request.POST)
if form.is_valid():
player_to_sign = form.cleaned_data['squad_to_player']
player_to_sign.squad = squad
player_to_sign.save()
return redirect('fanta_soccer_app:squad', squad_id=squad_id)
else:
form = AddPlayerToSquadForm()
context = {"squad": squad, "form": form}
return render(request, 'fanta_soccer_app/add_player.html', context)
Related
I have a functioning dropdown menu filled with some objects from a database. When a user selects one, they should be directed to the objects appropriate page. Each object is a "Workout" and each workout has "Exercises". It is working, however, I am struggling to get the appropriate 'id' from the object that has been selected by the user. Below I will provide the view, template, url, and form.
view.py
#login_required
def routines(request):
"""This view will provide a dropdown list of all of the workouts of a specific user.
Once one is selected, the user will hit a submit button.
After submitted, a new page will show all the exercises for that workout"""
if request.method == "GET":
#if we have a GET response, provide a form with filled data
form = RoutineForm(request.GET, user=request.user)
#data needs to equal the object that was selected from the GET form
data = 1
if form.is_valid():
data=form.cleaned_data("workout")
form.instance.owner = request.user #make sure we have the right user
form.save()
return HttpResponseRedirect('routine_workouts_app:routines')
context = {'form':form, 'data':data}
return render(request, 'routine_workouts_app/routines.html', context)
#login_required
def routine(request, data):
"""I think this is the view that will display the information of a workout as of now.
Should be similar to the workout view."""
a_workout = Workout.objects.get(id=data)
#Make sure the topic belongs to the current user
if a_workout.owner != request.user:
raise Http404
exercises = a_workout.exercise_set.order_by('exercise_name')
context = {'a_workout': a_workout, 'exercises':exercises}
return render(request, 'routine_workouts_app/routine.html', context)
routines.html
<form action="{% url 'routine_workouts_app:routine' data %}" method="get">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Make Workout</button>
</form>
urls
path('routines/', views.routines, name='routines'), #this one deals with selecting a workout and submitting a form
#via routine/ shows the specific workout. just like workouts/int
path('routines/<int:data>/', views.routine, name='routine'), #this one displays the information based on the form
and the forms.py
class RoutineForm(forms.ModelForm):
"""This creates a form to work for Routine making"""
def __init__(self, *args, user=None, **kwargs): #this code helps to manage ownership of workouts.
super().__init__(*args, **kwargs)
self.fields['workout'].queryset = Workout.objects.filter(owner=user)
#info comes from the Routine model. Only field is 'workout' and labels is how we are going to identify the field on the webpage
class Meta:
model = Routine
fields = ['workout']
labels = {'workout': 'Workout'}
Basically, I would like to have data equal to the object that the user has selected in my routines function in views.py. From there, I can obtain the objects ID and direct the user to the right page. As of now, no matter when the use picks, they will be directed to the "Workout" with the ID of 1 (hence, why it is functioning but not to my desire).
I appreciate the help in advance. Also, may anyone recommend any textbooks that help improve Django skills such as this?
picture for reference:
You can use the redirect shortcut to make a redirection to a named url with arguments after validating the form. The argument are the workout id, which is stored in the routine instance created by the form.
<form action="{% url 'routine_workouts_app:routines' %}" method="get">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Make Workout</button>
</form>
from django.shortcuts import redirect
#login_required
def routines(request):
form = form = RoutineForm(request.GET or None, user=request.user)
if form.is_valid():
# workout_instance = form.cleaned_data("workout")
routine_instance = form.save(commit=False)
routine_instance.owner = request.user # make sure we have the right user
routine_instance.save()
return redirect('routine_workouts_app:routine', args=(routine_instance.workout.pk,))
context = {'form': form}
return render(request, 'routine_workouts_app/routines.html', context)
I found multiple answers to this same questions but unfortunately, I can't seem to figure it out :(
The form has a drop-down list for the 'subcategory' field in my model 'PhysicalPart', the values of the 'subcategory' field are updated dynamically upon the form creation (using a 'category' parameter).
Unfortunately, I can't get the drop-down to show all subcategories AND have the one from the database selected at the same time. I can't seem to retrieve the 'short_description' value either from the database.
It used to work before I learned about UpdateView class and decided to use it instead...
Any insight on how-to workaround my problem would be appreciated!
forms.py
class PartForm(forms.ModelForm):
subcategory = forms.ChoiceField(choices=[])
class Meta:
model = PhysicalPart
fields = ['subcategory', 'short_description']
views.py
class PartUpdate(UpdateView):
model = PhysicalPart
template_name = 'part_update.html'
form_class = PartForm
def post(self, request, *args, **kwargs):
# Load model instance
self.object = self.get_object()
# Load form
form = super(PartUpdate, self).get_form(self.form_class)
# Populating subcategory choices
form.fields['subcategory'].choices = SubcategoryFilter[self.object.category]
# Check if form valid and save data
if form.is_valid():
form.save()
return redirect('part-list')
# Update context before rendering
context = self.get_context_data(**kwargs)
context['part_id'] = self.object.pk
context['part_category'] = self.object.category
context['manufacturing_list'] = self.object.manufacturing.all()
return render(request, self.template_name, context)
html
<form action="{% url 'part-update' pk=part_id category=part_category %}" method="post" style="display: inline">
{% csrf_token %}
<div class="form">
<p class="font-weight-bold">Type</br>
{{ form.subcategory }}
</p>
</div>
<div class="form">
<p class="font-weight-bold">Short Description</br>
{{ form.short_description }}
</p>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
<form action="{% url 'part-list' %}" style="display: inline">
<button type="submit" class="btn btn-danger">Cancel</button>
</form>
My problem was that I did not differentiate the "GET" versus the "POST" calls in the UpdateView class, I was trying to do everything in the post() method. It took me a while to figure it out but now I think it's clear.
I originally used the get() method but I realize that get_context_data() was better suited as it automatically loads most of the context (eg. the instance and the form), instead of having to do everything from scratch in the get() method.
Scrubbing through the code of the UpdateView class here, it also seemed necessary to add ModelFormMixin into the declaration of the PartUpdate class so that the get_context_data() method automatically loads the form associated to the target model/instance (else it looks like it won't do it).
Here is my updated views.py code:
class PartUpdate(UpdateView, ModelFormMixin):
model = PhysicalPart
template_name = 'part_update.html'
form_class = PartForm
success_url = reverse_lazy('part-list')
def get_context_data(self, **kwargs):
# Load context from GET request
context = super(PartUpdate, self).get_context_data(**kwargs)
# Get id from PhysicalPart instance
context['part_id'] = self.object.id
# Get category from PhysicalPart instance
context['part_category'] = self.object.category
# Add choices to form 'subcategory' field
context['form'].fields['subcategory'].choices = SubcategoryFilter[self.object.category]
# Return context to be used in form view
return context
def post(self, request, *args, **kwargs):
# Get instance of PhysicalPart
self.object = self.get_object()
# Load form
form = self.get_form()
# Add choices to form 'subcategory' field
form.fields['subcategory'].choices = SubcategoryFilter[self.object.category]
# Check if form is valid and save PhysicalPart instance
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
From my understanding you are trying to edit an instance. This is how you do it in Django, it should autopopulate your inputs with the proper values :
my_record = MyModel.objects.get(id=XXX)
form = MyModelForm(instance=my_record)
More details on this answer : how to edit model data using django forms
If your models are properly done (with relations) you shouldn't need to provide the choices for the Select.
I want to make a comments section in my post detail page. For that i was watching a tutorial for that on youtube. Here the tutorial uses function based view and i want to make it class based view.
Can anyone please help me convert this to class based view
in function based view
def post_detail(request, slug=None):
instance = get_object_or_404(Post, slug=None)
content_type = ContentType.objects.get_for_model(Post)
obj_id = Post.id
comments = Comment.objects.filter(content_type=content_type, object_id=obj_id)
context = {
"title": instance.title,
"instance": instance,
"comments": comments,
}
return render(request, "post_detail.html", context)
so far i tried this way to make it class based which i know is wrong.
class PostDetailView(LoginRequiredMixin,DetailView):
model = Post
template_name = 'posts/post_detail.html'
content_type = ContentType.objects.get_for_model(Post)
obj_id = Post.id
comments = Comment.objects.filter(content_type=content_type, object_id=obj_id)
But this gives me error something like this
return int(value) TypeError: int() argument must be a string, a bytes-like object or a number, not 'DeferredAttribute'
There are issues in your function view as well. Looking at your class based view, if you want to display comments in your detail view, then you don't want any of those thing, all you need this,
class PostDetailView(DetailView):
model = Post
template_name = 'posts/post_detail.html'
To show comments related to the post all you need to use your related name, in your comment model you should name something like this,
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='postcomments')
# ... (other code)
To show this in html, all you need to do this,
{% for comment in post.postcomments.all %}
{{comment.text}} #add according to your model
{% endfor %}
To create comment in the same page you need to add some extra things, Make sure you have comment form as well.
class PostDetailView(DetailView):
model = Post
template_name = 'post/post_detail.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['commentform'] = CommentForm()
return context
def post(self, request, pk):
post = get_object_or_404(Post, pk=pk) #Assuming you have <int:pk> in url, if you have slug change pk to slug
form = CommentForm(request.POST) #Add your comment form
if form.is_valid():
obj = form.save(commit=False)
obj.post = post
obj.user = self.request.user
obj.save()
return redirect('detail', post.pk) # Correct it according your urlpattern name
In your same post detail html you can simply add your same html as you have used in other form templates,
<form method="POST" action="" >
{% csrf_token %}
<p> {{form}} </p>
<button type="submit"> Create </button>
</form>
data cannot be saved in form.I wrote html like
<form action="/app/save" method="POST">
<input id="classname" name="classname">
<input id="score" name="score">
<input id="course" name="course">
<button type="submit">SEND</button>
</form>
in forms.py
from django import forms
from .models import Student
class SaveForm(forms.ModelForm):
class Meta:
model = Student
fields = ("classname", "score", "course")
in views.py
#csrf_exempt
def save(request):
save_form = SaveForm(request.POST or None)
print(save_form.is_valid())
if request.method == "POST" and save_form.is_valid():
item = save_form.save(commit=False)
classname = request.POST.get("classname", "")
score = request.POST.get("score", "")
course = request.POST.get("course", "")
item.classname = classname
item.score = score
item.course = course
item.save()
return render(request, 'index.html')
in models.py
from django.db import models
class Student(models.Model):
classname = models.CharField(max_length=100)
score = models.CharField(max_length=100)
course = models.CharField(max_length=100)
When I run this codes ,put data A in classname& 80 in score&Math in course of html and put SEND button,save method can be called but data cannot be saved.print(save_form.is_valid()) shows False.I really cannot understand why I can't send data.What is wrong in my codes?How should I fix this?
Okay a few things here;
Why is score a charfield? If it's a %, shouldn't it be an int?
Student model doesn't have any foreign keys, so there's really no need to use commit = false in your save.
Creating inputs for each item on your form in html is definitely the long way to do it. The best way to do this in your html is simply to use {{form}}. To do this, you'll need to pass the form as a variable in context in your views.py.
Without being mean, are you very early in to the django beginner tutorial? If so, I would recommend moving forward through that before trying to continue your current project. It'll really help you. Here's what I would suggest as a views.py
def save(request):
if request.method == "POST":
save_form = SaveForm(request.POST)
print(save_form.is_valid())
if(save_form.is_valid()):
save_form.save()
return HttpResponseRedirect('New-destination-URL')
save_form = SaveForm()
context['form'] = save_form
return render(request, context, 'index.html')
and in index.html:
form action="/app/save" method="POST">
{{form}}
</form>
You do it wrong, you have to handle both situations - when you are on page at first time, and time when form is filled with data.
Try to change your views to this :
if request.method == "POST":
save_form = SaveForm(request.POST)
...
else:
save_form = SaveForm()
return render(request, 'index.html', {'save_form': save_form)
and then try to put this in template:
<form action="/app/save" method="POST">
{{ save_form.as_p }}
<button type="submit">SEND</button>
</form>
I have a little problem I've create a edit form to update the existing records. The form is displaying correctly, but when I click the edit button to update the records, the url redirecting me and record is not updated.
My views.py resposible for edit:
#login_required
def szczegoly_pracownik(request, id):
link_pracownik = get_object_or_404(Cudzoziemiec, id=id)
return render(request, 'cudzoziemiec/szczegoly_pracownik.html', {'link_pracownik': link_pracownik})
#login_required
def edycja_pracownika(request, id):
link_pracownik = get_object_or_404(Cudzoziemiec, id=id)
if request.method == 'POST':
edycja_pracownika = CudzoziemiecForm(request.POST, instance=link_pracownik)
if edycja_pracownika.is_valid():
link_pracownik = edycja_pracownika.save(commit=False)
link_pracownik.save()
return render('szczegoly_pracownik', id=link_pracownik.id)
else:
edycja_pracownika = CudzoziemiecForm(request.user, instance=link_pracownik)
return render(request, 'cudzoziemiec/edycja_pracownika.html', {'edycja_pracownika': edycja_pracownika})
The def szczegoly_pracownika is responsible for displaying the detail view
File edycja_pracownika.html
{% if request.user.is_authenticated %}
<form action="." method="post">
{{ edycja_pracownika.as_p }}
{% csrf_token %}
<div class="float-right">
<p><input type="submit" value="Edytuj" ></p>
{% endif %}
and the urls.py responsible for detail view and edit view
...
path('pracownik/<id>', edycja_pracownika, name='edycja_pracownika'),
path('pracownik/<id>/', szczegoly_pracownik, name='szczegoly_pracownik'),
Maybe somebody know where is the bug?
EDIT:
forms.py
class CudzoziemiecForm(forms.ModelForm):
class Meta:
model = Cudzoziemiec
fields = ('nazwa','imie', 'nazwisko','obywatelstwo', 'data_ur','miejsce_ur','paszport','biometria', 'data_start_pasz', 'data_koniec_pasz', 'dok_pobytowy','cel_wizy', 'data_start_pobyt', 'data_koniec_pobyt')
def __init__(self, user, *args, **kwargs):
super(CudzoziemiecForm, self).__init__(*args, **kwargs)
self.fields['nazwa'].queryset = user.firma_set.all()
self.user = user
def save(self, commit=True):
instance = super(CudzoziemiecForm, self).save(commit=False)
instance.user = self.user
if commit:
instance.save()
return instance
I don't arrive to see the real problem but a couple of things that I would try.
You are using the same almost the same url for different pages. It should not be a problem, but I see that as a possible pitfall. Why don't use something like r'^pracownik/edytuj/$' for the editing form? (sorry for my attempt to make a Polish url :-) ).
Maybe that can avoid possible problems and help to clarify the error.
Also when you say that:
the url redirecting me
Do you mean you are redirected to the form again or to the detail page?