Many to many relationships in django; refering from one object to another - django

Ok guys, I got two different models:
class topic(models.Model):
learningObjectivesTopic = models.ManyToManyField(learningObjective, verbose_name = "Lernziel")
topic = models.TextField(verbose_name = 'Thema')
and
class learningObjective(models.Model):
learningObjectives = models.TextField(verbose_name = 'Lernziel')
Pretty basic. My aim is to create entrys for the attribute "topic" and then refer to them when I want to create a new learning objective. My views obviously don't work the way I want them to work but I will post them to explain later on what my aim was.
from django.shortcuts import render
from programm.models import *
from django.contrib.auth.decorators import login_required
#login_required(login_url='login')
def lernziel(request):
return render(request, 'lernziel.html', {'topic': topic.objects.all()})
#login_required(login_url='login')
def create_lernziel(request):
neuesLernziel=learningObjective(learningObjectives=request.POST["Lernziel"])
neuesLernziel.save()
neuesLernziel_Topic=topic(topic=request.POST["Thema"])
neuesLernziel_Topic.save()
neuesLernziel_Topic.learningObjectivesTopic.add(neuesLernziel)
return render(request, 'lernziel.html', {'topic': topic.objects.all()})
#login_required(login_url='login')
def themen(request):
return render(request, 'themen.html', {'thema': topic.objects.all()})
#login_required(login_url='login')
def create_themen(request):
neueThemen=topic(topic=request.POST['Thema'])
neueThemen.save()
return render(request, 'themen.html', {'thema': topic.objects.all()})
I thought with this line:
neuesLernziel_Topic.learningObjectivesTopic.add(neuesLernziel)
I could refer a learning objective, which I am about to create, to and existing topic. But how it seems, my django view just creates a new entry with a new id instead of using an old entry with the same value and an old id. To make it more clear: I have: topic1 and topic2. And I want to refer now 4 learning objectives. For instance lObj 1 and 3 to topic 1 and lObj 2 and 4 to topic 2. What happens is, that at the moment django created 4 new entrys in the topic table. It now contains 3 entrys with the value topic1 and 3 entrys with the value topic2. It should have been still 2 entrys and in the manytomany table it should use the odl id's. If you need more information or if something is unclear please write it :)
Btw I am receiving the data from these two templates:
lernziel.html:
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE|default:"de-de" }}" >
<head>
<h1 align = "center">Lernziele</h1>
</head>
<body>
<form action="{% url 'create_lernziel' %}" method="post">
{% csrf_token %}
<br>Hallo Benutzer: {{ user.username }}</br>
Lernziel: <textarea name="Lernziel" rows="3" cols="45" ></textarea>
<p>
<select name="Thema" size="3">
{% for topic_ in topic %}
<option>{{ topic_.topic }}</option>
{% endfor %}
</select>
</p>
<input type="submit" value="Absenden" />
</form>
<table border="1">
<th>Lernziel</th>
<th>Thema</th>
{% for topic_ in topic %}
<tr>
{% for lObj in topic_.learningObjectivesTopic.all %}
<td>{{ lObj }}</td>
{% endfor %}
<td>{{ topic_.topic }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
and themen.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<br>Hallo Benutzer: {{ user.username }}</br>
<form action="{% url 'create_themen' %}" method="post">
{% csrf_token %}
<br>Thema: <textarea name="Thema" rows="3" cols="45"></textarea></br>
<input type="submit" value="Absenden" />
</form>
</br>
<table border="1">
<th>Thema</th>
{% for thema_ in thema %}
<tr>
<td>{{ thema_.topic }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>

The problem is not with that line: it is with the previous two lines, which explicitly create a new Topic. If you want to associate your objective with an existing Topic, you need to use get to get it from the database, rather than instantiating a new one and calling save().
neuesLernziel=learningObjective(learningObjectives=request.POST["Lernziel"])
neuesLernziel.save()
neuesLernziel_Topic = Topic.objects.get(topic=request.POST['Thema'])
neuesLernziel_Topic.learningObjectivesTopic.add(neuesLernziel)
You probably want to post/query by an ID, rather than the text of "topic", though.

Related

Get Data Problem with Foreignkey Models QuerySet (Django)

I am making django practising. I found a repo and edited to myself. When i came to the get data from models with foreignkey, i get query instead of data. I think my function is a little bit spagetti or my template file.
Full repo is here https://github.com/eseymenler/demo2
Here is the output of my codes. First red square is query, but i want second red square datas.
Hasta Adı: {{ patient.name }}
Hastanın Sağlık Problemi: {{ prob }}
Hastanın Boyu: {{ vital }}
Hastanın Sigara Kullanım Durumu: {{ social }}
First data {{ patient.name }} is very good for me. Thats what i want.
But when i get write {{ prob.problem }} it gives me nothing. So where is my fault.
But also when i make for loop like this, i get data which i want. So how can i fix this.
{% for y in prob %}
{{ y.problem }}
{% endfor %}
And my views.py
#login_required()
def patienttumbilgiListView(request, id):
patient = Patient.objects.get(aadhaarId = id)
prob = ProblemList.objects.filter(patient = patient)
vital = VitalSign.objects.filter(patient = patient)
social = SocialHistory.objects.filter(patient = patient)
return render(
request, 'patient_records/patient-problem-tum.html',
context={'prob': prob, 'vital': vital, 'social': social, 'patient': patient })
and my template file
{% extends 'base.html' %}
{% load static %}
{% load bootstrap4 %}
{% load i18n %}
{% block title %}problem-list-view{% endblock title %}
{% block content %}
<div class="container">
Hasta Adı: {{ patient.name }}</br>
Hastanın Sağlık Problemi: {{ prob }}<br>
Hastanın Boyu: {{ vital }}<br>
Hastanın Sigara Kullanım Durumu: {{ social }} <br></div>
<br>
<br>
<div class="container">
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
overflow:hidden;padding:10px 5px;word-break:normal;}
.tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-0lax{text-align:left;vertical-align:top}
</style>
<table class="tg">
<thead>
<tr>
<th class="tg-0lax">Hasta Adı:</th>
<th class="tg-0lax">{{ patient.name }}</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tg-0lax">Hastanın Sağlık Problemi:</td>
<td class="tg-0lax">{% for y in prob %}<br> <br> {{ y.problem }}<br> <br> <br>{% endfor %}</td>
</tr>
<tr>
<td class="tg-0lax">Hastanın Boyu:</td>
<td class="tg-0lax">{% for x in vital %}<br> <br> {{ x.height }}<br> <br> <br>{% endfor %}</td>
</tr>
<tr>
<td class="tg-0lax">Sigara Kullanımı:</td>
<td class="tg-0lax">{% for item in social %}<br> <br><br> {{ item.tobacco_smoking_status }}<br> <br> <br>{% endfor %}</td>
</tr>
</tbody>
</table>
</div>
<br>
<br>
{% endblock %}
Roughly What You Had
So your code is:
#login_required()
def patienttumbilgiListView(request, id):
patient = Patient.objects.get(aadhaarId = id)
prob = ProblemList.objects.filter(patient = patient)
vital = VitalSign.objects.filter(patient = patient)
social = SocialHistory.objects.filter(patient = patient)
return render(
request, 'patient_records/patient-problem-tum.html',
context={'prob': prob, 'vital': vital, 'social': social, 'patient': patient })
{% for y in prob %}
{{y.problem}}
{% endfor %}
{% for x in vital %}
{{x.height}}
{% endfor %}
{% for item in social %}
{{item.tobacco_smoking_status}}
{% endfor %}
Some Friendly Suggestions
"id" is a reserved word / function - I suggest changing id to maybe pid in your code.
Return an error to your view - You don't necessarily have to make this change, but I think its a good idea, and can even help with troubleshooting. I mean you should probably be returning an error or something to this chart if you can't find these objects with those ids in your database.
"get()" throws a DoesNotExist error if the id isn't found - You should catch the error and return it to the console you are making or do something like that.
views.py
#login_required()
def patienttumbilgiListView(request, pid):
context = {}
try:
patient = Patient.objects.get(aadhaarId = pid)
except DoesNotExist:
context['error'] = f"Could not find patient with id '{pid}'."
else:
context['prob'] = ProblemList.objects.filter(patient=patient)
context['vital'] = VitalSign.objects.filter(patient=patient)
context['social'] = SocialHistory.objects.filter(patient=patient)
context['patient'] = patient
return render(request, 'patient_records/patient-problem-tum.html', context=context)
template.html
{% if error %}
{{error}}
{% endif %}
{% for y in prob %}
{{y.problem}}
{% empty %}
Could not find problems.
{% endfor %}
{% for x in vital %}
{{x.height}}
{% empty %}
Could not find vitals.
{% endfor %}
{% for item in social %}
{{item.tobacco_smoking_status}}
{% empty %}
Could not find socials.
{% endfor %}
What Your Problem Is And How To Solve It
I am making django practising. I found a repo and edited to myself. When i came to the get data from models with foreignkey, i get query instead of data.
Based on this quote, I am assuming you want to know why the data is being returned as a queryset instead of as a singular result. From the docs:
filter() will always give you a QuerySet, even if only a single object matches the query - in this case, it will be a QuerySet containing a single element. If you know there is only one object that matches your query, you can use the get() method on a Manager which returns the object directly: one_entry = Entry.objects.get(pk=1).
Which is why you should use the for loop in your template.html. If you don't want to use that, then you should preprocess this data either by using get() instead of filter() or simply, in the context, send the [0] element to the template from your view.
So, to clarify, if you want to use {{prob.problem}} in your template, you need to change your view to either do prob = ProblemList.objects.filter(patient = patient)[0] or prob = ProblemList.objects.get(patient = patient). If you choose the first, you should check the length (prob = prob[0] if len(prob) == 1 else None), otherwise, if you choose the latter, you should wrap it with a try, except, else clause like this:
try:
prob = ProblemList.objects.get(patient=patient)
except DoesNotExist:
context['error'] = "Could not find ProblemList."
print("Does Not Exist")
else:
context['prob'] = prob
or you could just use the for loop that you have in place, I don't know how many entries you are expecting the query set to have per patient.
If I need to further clarify anything, please let me know.

django keep populating Form data in table

I am pretty new to django I need to keep adding form data in to the html table in same page. I can populate one time but i need to keep adding each time.
refer below gif:
sample data
What I need is i need to add the data i have added second time to populate in the table in second row.
please help me on this
Form.py
from django import forms
class vm_provisioning_form(forms.Form):
name = forms.CharField()
email = forms.EmailField(
)
views.py
from django.shortcuts import render
from django.http import HttpResponse
from Forms import forms
def vm_provisioning(request):
form_vm_provisoning_info = forms.vm_provisioning_form()
hidden_values = []
if request.method == 'POST':
values = []
form_vm_provisoning_info = forms.vm_provisioning_form(request.POST)
if form_vm_provisoning_info.is_valid():
# DO SOMETHING CODE
print("VALIDATION SUCCESS!")
name = form_vm_provisoning_info.cleaned_data['name']
email = form_vm_provisoning_info.cleaned_data['email']
values.append([name, email])
return render(request, 'Forms_template/vm_provisioning/vm_provisioning_form.html', {'forms': form_vm_provisoning_info,'data': values })
return render(request, 'Forms_template/vm_provisioning/vm_provisioning_form.html', {'forms': form_vm_provisoning_info})
form_vm_provisoning_info.html
<!-- templates/vm_provisoning/form_vm_provisoning_info.html -->
<!DOCTYPE html>
{% extends 'Forms_template/base.html' %}
{% block title %}Login in registration{% endblock %}
{% block content %}
<h1> Vm provisoning form</h1>
<div class="container">
<h1>Please Fill details here!</h1>
<form method="POST">
{{ forms.as_p }}
{% csrf_token %}
<section>
<!-- hidden_data= forms.CharField(widget=forms.HiddenInput(), required=False) -->
{% if data %}
<table border="1">
<tr>
<th>Name</th>
<th>email</th>
</tr>
<!-- <p>{{ data }}</p> -->
{% for mapdata in data %}
<!-- <p>{{ mapdata }}</p> -->
<tr>
<td>{{ mapdata.0 }} </td>
<td>{{ mapdata.1 }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</section>
<input type="submit" class='btn btn-primary' value="Submit">
</form>
</div>
{% endblock %}
Take a look at your views. This might be because you are assigning new values list everytime there is a POST request sent, instead try giving it above, also you must send the values list to the GET method also, so it always displays the data.
def vm_provisioning(request):
form_vm_provisoning_info = forms.vm_provisioning_form()
hidden_values = []
values = []
if request.method == 'POST':
form_vm_provisoning_info = forms.vm_provisioning_form(request.POST)
if form_vm_provisoning_info.is_valid():
# DO SOMETHING CODE
print("VALIDATION SUCCESS!")
name = form_vm_provisoning_info.cleaned_data['name']
email = form_vm_provisoning_info.cleaned_data['email']
values.append([name, email])
return render(request, 'Forms_template/vm_provisioning/vm_provisioning_form.html', {'forms': form_vm_provisoning_info,'data': values })
return render(request, 'Forms_template/vm_provisioning/vm_provisioning_form.html', {'forms': form_vm_provisoning_info,'data': values})

Display users' images in Django

I am learning Django and currently trying to make a simple website where users can post topics and stuff. What I want is only the uploaded images that belong to a certain topic to display when you open the topic. But I am so stuck at it now I can't get any image come up on the pages. I've tried many different things but none's worked so far. I can't get my head around how exactly the template should look like and how do I link it to the topic page...and my view is probably far from right but I've tried a lot of things and it's all got a bit messy...pretty much it's been 2 days of hassle and I will appreciate any directions
Thank you in advance
This is what i have:
models.py
class Image(models.Model):
"""images representation"""
image=models.ForeignKey(Topic, on_delete=models.CASCADE)
image=models.ImageField(upload_to= 'media/')
caption=models.CharField(max_length= 100)
uploaded_at=models.DateTimeField(auto_now_add=True)
views.py
def upload_image(request, topic_id):
"""Upload images"""
topic=Topic.objects.get(id=topic_id)
if request.method != 'POST':
# No data submitted; creaete a blank form
form=ImageForm()
else:
# Data submitted; Process data
form=ImageForm(request.POST, request.FILES)
if form.is_valid():
instance=form.save(commit=FALSE)
instance.topic=topic
instance.save()
return HttpResponseRedirect(reverse('the_horror:topic',
args=[topic_id]))
images=Image.objects.all()
context={'form':form, 'topic':topic, 'images':images}
return render(request, 'the_horror/topic.html', context)
upload_image.html
{% extends 'the_horror/base.html' %}
{% block content %}
{% if images %}
<ul>
{% for image in images %}
<li>
<a href="{{image.image.url}}">
<img source="{{image.image.url}}" alt="sth"></a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No documents.</p>
{% endif %}
<form action="{% url 'upload_image' %}" method= "post" enctype= "multipart/form-data">
{% csrf_token %}
<input type="file" name="image"/>
<input type="submit" name="submit" value="Upload"/>
<button type="submit">name="upload photo"</button>
</form>
As i said i am not sure how to pass the images to the topic template
topic.html
{% extends 'the_horror/base.html' %}
{% block content %}
<p>Topic:{{topic}}</p>
<p>
edit topic
</p>
<p>Entries</p>
<p>
Add a new entry
</p>
<ul>
{% for entry in entries %}
<li>
<p>{{entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{entry.text|linebreaks}}</p>
<p>
<image class= "Image" source={{>
</p>
<p>
edit entry
</p>
</li>
{% empty %}
<li>
There are no entries for this topic yet.
</li>
{% endfor %}
</ul>
{% endblock content %}
urls.py
path('upload_image/<topic_id>', views.upload_image, name= 'upload_image'),
# Single topic page
On your models.py you don't need to write media on your upload_to option, what you have to do is to add to your urls.py static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) here
urls.py
urlpatterns = [
...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
On your upload_image.html why are you creating 2 input. Personally I use an input to take the image file
upload_image.html
...
<input name="image" type="file">
...
then in the views you started well but I will do as follow to store the image
views.py
def upload_image(request, topic_id):
...
imageObject = Image.objects.create()
image = request.FILES['image']
imageObject.image = image
...
Finally, you will get your image this way
topic.html
<img src="{{MEDIA_URL}}{{imageObject.image}}" alt="image">
Usually it works, I made several projects with this method or similar methods, if you still got error or question let me know

Django - How to delete a object directly from a button in a table

(sorry for my bad english)
I need to delete an object, but directly from a list of the objects that y have in my template.
I have a work orders, that have spare parts but i don't know how to create the deleteview for the spare parts using only a buton in the detailview of the work order. The idea is that the user make click in the Delete button.
This is the model of the Spare Parts
class OrderSparePart(models.Model):
# Relations
workorder = models.ForeignKey(
WorkOrder,
verbose_name=_('order'),
)
# Attributes - Mandatory
spare_part = models.CharField(
max_length=80,
verbose_name=_('spare part'),
)
# Attributes - Optional
price = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True,
verbose_name=_('price'),
)
# Object Manager
# Custom Properties
# Methods
def get_absolute_url(self):
return reverse('work_orders:detail', kwargs={'order_id': self.workorder.id})
# Meta and String
class Meta:
verbose_name = _("order spare part")
verbose_name_plural = _("order spare parts")
This is where is showed in the template
{% if spare_parts %}
<table class="table">
<thead>
<tr>
<th>{% trans "Spare Part" %}</th>
<th>{% trans "Price" %}</th>
<th>{% trans "Delete" %}</th>
</tr>
</thead>
<tbody>
{% for part in spare_parts %}
<tr>
<td><i class="fa fa-gear"></i> {{ part.spare_part }}</td>
{% if part.price %}
<td>$ {{ part.price }}</td>
{% else %}
<td></td>
{% endif %}
<td><i class="fa fa-trash"></i></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>NO HAY REPUESTOS ASENTADOS AÚN</p>
{% endif %}
The the idea is use the to delete the spare part.
how i have to make the deleteview and the link to this???
Thanks!
here in fa fa-thrash pass the id and the URL as I did it:-
{% if spare_parts %}
<table class="table">
<thead>
<tr>
<th>{% trans "Spare Part" %}</th>
<th>{% trans "Price" %}</th>
<th>{% trans "Delete" %}</th>
</tr>
</thead>
<tbody>
{% for part in spare_parts %}
<tr>
<td><i class="fa fa-gear"></i> {{ part.spare_part }}</td>
{% if part.price %}
<td>$ {{ part.price }}</td>
{% else %}
<td></td>
{% endif %}
<td></i></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>NO HAY REPUESTOS ASENTADOS AÚN</p>
{% endif %}
ur url would be sonething like that:
url(r'^delete/(?P<part_id>[0-9]+)/$', view.function, name='delete_view'),
in ur view:
def function(request,part_id =None):
object = YourModel.objects.get(id=part_id)
object.delete()
return render(request,'ur template where you want to redirect')
In your html template inside for loop use the form tag inside <td> to create delete button as below (css class will work if you are using bootstrap3):
<form action="{% url 'delete_view' pk=part.pk %}" method="POST">
{% csrf_token %}
<input class="btn btn-default btn-danger" type="submit" value="Delete"/>
</form>
add urlpattern in urls.py
url(r'^delete-entry/(?P<pk>\d+)/$', views.DeleteView.as_view(), name='delete_view'),
delete view will be like below in views.py
class DeleteView(SuccessMessageMixin, DeleteView):
model = OrderSparePart
success_url = '/'
success_message = "deleted..."
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
name = self.object.name
request.session['name'] = name # name will be change according to your need
message = request.session['name'] + ' deleted successfully'
messages.success(self.request, message)
return super(DeleteView, self).delete(request, *args, **kwargs)
Note: import necessary imports shown in links or you need not to worry if you are using IDE such as pyCharm it will prompt you which import to make.
My solutions works best for django 4.0.3 and is the combination of gahan and Abi waqas answers. Use this one if you are using django 3 or above
Add the following to views.py
def delete_object_function(request, id):
# OrderSparePart is the Model of which the object is present
ob = OrderSparePart.objects.get(id=id)
ob.delete()
return redirect('page-delete.html') # for best results, redirect to the same page from where delete function is called
Add the following to urls.py
path('page-delete/<int:id>', views.delete_object_function, name='delete_object'),
Add the following code to the django template from where the delete function is to be called.
Let's say page-delete.html
<form action="{% url 'delete_object' id=part.id %}" method="post">
{% csrf_token %}
<button class="btn btn-danger" type="submit" ><i class="fa fa-trash"></i></button>
</form>
This works as I've used this solution in my own code.

Editing a search view in Django

Hello I am working in django how to use a search form from Django documents site. Here are some information below.
Now, is there a way I can produce a part search for title? For example if I wanted to search for a book called "Apress", But instead of typing the whole word I just wrote "ap" to get Apress. Is there a way how to achieve this solution?
from django.db.models import Q
from django.shortcuts import render_to_response
from models import Book
#views.py
def search(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(title__icontains=query) |
Q(authors__first_name__icontains=query) |
Q(authors__last_name__icontains=query)
)
results = Book.objects.filter(qset).distinct()
else:
results = []
return render_to_response("books/search.html", {
"results": results,
"query": query
})
#search html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>Search{% if query %} Results{% endif %}</title>
</head>
<body>
<h1>Search</h1>
<form action="." method="GET">
<label for="q">Search: </label>
<input type="text" name="q" value="{{ query|escape }}">
<input type="submit" value="Search">
</form>
{% if query %}
<h2>Results for "{{ query|escape }}":</h2>
{% if results %}
<ul>
{% for book in results %}
<li>{{ book|escape }}</l1>
{% endfor %}
</ul>
{% else %}
<p>No books found</p>
{% endif %}
{% endif %}
</body>
</html>
If you want to match on the beginning of a field, you can use startswith or istartswith if you want it to be case sensitive. icontains which you are using now will allow matches even within strings, ie. 'arry' will match 'Harry'. While startswith will allow 'Har' to match 'Harry', but not 'ArHarHar'