I'm fairly new to Django. I have a database with Events. I want to display the name of the Organisation where org_id is the foreign key in the events model. My approach was to load in all the objects and then do some iterating through them but I get no output on the website. I feel like its got to do something with the templating language
models.py
class Organisation(models.Model):
org_id=AutoSlugField(unique=True)
name = models.CharField(max_length=200)
email=models.EmailField(max_length = 250)
class Event(models.Model):
event_id = AutoSlugField(unique=True)
name = models.CharField(max_length=100)
date = models.DateField()
event_category = models.CharField(max_length=50)
duration= models.IntegerField()
org_id = models.ForeignKey(Organisation,on_delete=models.CASCADE)
maxparticipants= models.IntegerField()
teacher_id=models.ForeignKey(User,on_delete=models.CASCADE)
relevant snippet from event_details.html
<h3>Hosting Body</h3>
{% for org in orgs%}
{%if org.org_id == event.org_id %}
<p>{{org.name}}</p>
{%endif%}
{%endfor%}
<h3>Date</h3>
<p>{{event.date}}</p>
views.py
def event_details(request,pk):
event=Event.objects.get(event_id=pk)
orgs=Organisation.objects.all()
context={'event':event,'orgs':orgs}
return render(request,'event_details.html',context)
You can render the relevant organization with:
<h3>Hosting Body</h3>
<p>{{ event.org_id.name }}</p>
<h3>Date</h3>
<p>{{ event.date }}</p>
You can boost efficiency by fetching the Event and Organisation data in the same query with:
from django.shortcuts import get_object_or_404
def event_details(request, pk):
event = get_object_or_404(Event.objects.select_related('org_id'), event_id=pk)
context = {'event':event}
Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.
Note: Normally one does not add a suffix _id to a ForeignKey field, since Django
will automatically add a "twin" field with an _id suffix. Therefore it should
be org, instead of org_id.
in your template change it to:
<h3>Hosting Body</h3>
{% for org in orgs%}
{%if org.org_id == event.org_id.org_id %}
<p>{{org.name}}</p>
{%endif%}
{%endfor%}
<h3>Date</h3>
<p>{{event.date}}</p>
Related
models equipement :
class Equipement(models.Model):
nom_equipement=models.CharField(max_length=60)
qte_stock=models.IntegerField()
panne=models.ManyToManyField(Panne)
models intervention :
class Intervention(models.Model):
Titre_intervention = models.TextField(max_length=255)
date_intervention = models.DateField(auto_now_add=True)
type_panne = models.ForeignKey(Panne,on_delete=models.CASCADE)
etat = models.CharField(max_length=30)
description = models.TextField(max_length=255)
image = models.ImageField(blank=True,null=True,upload_to='medial/%Y/%m/%D')
equipements = models.ManyToManyField(Equipement)
clients = models.ForeignKey(Client,on_delete=models.CASCADE,default=True)
models intervention with relationship manytomany :
so when I add a new "intervention" it will add to table of association
I need to list all equipment of each intervention this is my view :
def mes_intervention(request):
if 'id_client' in request.session:
get_idClient=request.session['id_client']
Interv_client = Intervention.objects.all().filter(clients=get_idClient)
context = {
'intervention':Interv_client
}
return render(request, 'clients/mes-intervention.html',context)
and this is where I list all intervention into template html
As a side note, you have plurals and singulars a bit mixed up. A foreign key points to one model:
client = models.ForeignKey(Client,on_delete=models.CASCADE,default=True) # singular
A queryset returns multiple objects:
context = { 'interventions': Interv_client}
While you cannot call functions in templates with arguments, you can invoke object methods without arguments. So, you can in fact do this:
{% for item in interventions %}
...
{% for equipment in item.equipements.all %}
{{ equipment.qte_stock }}
{% endfor %}
{% endfor %}
I have extended the UserModel this way :
# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
# add additional fields in here
credit = models.IntegerField(default=200)
follow = models.ManyToManyField('self', related_name='follow')
def __str__(self):
return self.username
But I am stuck as to how I should add/remove a follower. I have created a view with :
#login_required
def follow(request, user_id):
user = get_object_or_404(CustomUser, pk=user_id)
if CustomUser.objects.filter(follow=user.pk).exists():
request.user.follow.remove(user)
else:
request.user.follow.add(user)
return redirect('profil', user_id)
Issue :
Let's say request.user.pk is 1 and user_id is 2.
For the add part (in the else), I would expect a new line in database with from_customuser_id=1 and to_customuser_id=2 however, it creates two lines:
one with from_customuser_id=1 and from_customuser_id=2 as expected
one with from_customuser_id=2 and from_customuser_id=1 which I don't need.
And for the remove part (in the if), I would expect it to only remove the line
from_customuser_id=1 and from_customuser_id=2
But it removes the two lines.
I read the doc about django models relations but didn't found how to solve this issue.
Question :
How should I update my code in order to have the add method to only insert one line with from_customuser_id=1, from_customuser_id=2 and the remove method to only delete this line (assuming the current user have the id 1).
Not sure if it is relevant but for sake of completeness this is the related part of my urls.py :
path('follow/<int:user_id>', views.follow, name='follow'),
path('unfollow/<int:user_id>', views.follow, name='unfollow'),
And this is how I call them in templates :
{% if follow %}
<a href="{% url 'follow' user_profil.id %}">
Unfollow {{ user_profil.username }}
</a>
{% else %}
<a href="{% url 'unfollow' user_profil.id %}">
Follow {{ user_profil.username }}
</a>
{% endif %}
When you have a ManyToManyField it essentially creates a relationship between both the objects. This also allows you to do reverse lookups.
For example:
class Person(models.Model):
name = model.CharField(max_length=100)
class Pet(models.Model):
owners = models.ManyToMany(Person, related_name="pets")
name = model.CharField(max_length=100)
bob = Person.objects.create(name="Bob")
john = Person.objects.create(name="John")
kitty_kat = Pet.objects.create(name="Kitty Kat")
kitty_kat.owners.set([bob, john])
According to these models one pet can be owned by multiple people, and one person can have multiple pets. So if I do
bob.pets.all() # I get kitty kat
kitty_kay.owners.all() # I get bob & john
When this relationship is supposed to be on the same model, you end up creating two relationships. One as the normal one & one for the reverse.
For example:
class Person(models.Model):
name = model.CharField(max_length=100)
followers = models.ManyToManyField('self', related_name='follow')
bob = Person.objects.create(name="Bob")
john = Person.objects.create(name="John")
john.followers.add(bob)
bob.follow.all() # I get john... notice I use follow and not followers
john.followers.all() # I get bob
In order to avoid this you can pass symmetrical=False to the field and one one row will be created
followers = models.ManyToManyField('self', related_name='+', symmetrical=False)
Setting the related_name to anything starting with + will also prevent reverse lookups (which you don't need in this case)
Assume u have two models. Let's name them computer and log_file.
I want to make a join so that a always display all computer objects, but if there is a related log_file object it is also added to the query result.
Which is the best way to achieve this? Relationship: one Computer has one log file. But it is possible it's not uploaded yet to the database. Only gets uploaded when my script throws an error.
Sorry for my beginner question, I'm new to Django.
following simplified models as an example:
Model 1: computer
id (pk)
computer name
mac address (unique)
Model 2: log file / max one for each computer
id (pk)
mac address
text field
required query: list of all computers and also the log file if there is any in the database. The join is made by the value of the mac address.
Think the SQL query would look like the following. But I am not able to translate this with the ORM.
SELECT *
FROM computer
LEFT JOIN log ON computer.mac_address = log_file.mac_address;
Thank you!
I found a work around by using a calculated field. Check the following link:
How to add a calculated field to a Django model
Everything below #property
I added some extra code to my model:
class ConnecThor(models.Model):
# Incrementing ID (created automatically)
hostname = models.CharField(max_length=40)
MAC_address = models.CharField(max_length=17,
default='00.00.00.00.00.00', help_text="Example format: 255.255.255.255",
primary_key=True)
IP_address = models.CharField(max_length=15, default='000.000.000.000', help_text="Example format: 192.148.0.1")
config_version = models.CharField(max_length=10, default='000')
creation_date = models.DateTimeField(auto_now_add=True, null=True,
editable=False) # timezone.now() gedaan tijdens make migrations
#property
def log_file(self):
try:
return LogFiles.objects.get(MAC_address=self.MAC_address)
except LogFiles.DoesNotExist:
print('logfile not found')
class Meta(object):
db_table = 'connecthor' # table name
And to my view:
{% if connecthor.log_file %}
{% if connecthor.log_file.viewed > 0 %}
<td>Approved</td>
{% else %}
<td>Failed update</td>
{% endif %}
{% else %}
<td><label class="badge badge-warning">Not Found</label></td>
{% endif %}
My code looks like this:
models.py
class Tag(models.Model):
name = models.CharField(max_length=42)
class Post(models.Model):
user = models.ForeignKey(User, related_name='post')
#...various fields...
tags = models.ManyToManyField(Tag, null=True)
views.py
posts = Post.objects.all().values('id', 'user', 'title')
tags_dict = {}
for post in posts: # Iteration? Why?
p = Post.objects.get(pk=[post['id']]) # one extra query? Why?
tags_dict[post['id']] = p.tags.all()
How am I supposed to create a dictionary with tags for each Post object with minimum set of queries? Is it possible to avoid iterating, too?
Yes you will need a loop. But you can save one extra query in each iteration, you don't need to get post object to get all its tags. You can directly query on Tag model to get tags related to post id:
for post in posts:
tags_dict[post['id']] = Tag.objects.filter(post__id=post['id'])
Or use Dict Comprehension for efficiency:
tags_dict = {post['id']: Tag.objects.filter(post__id=post['id']) for post in posts}
If you have Django version >= 1.4 and don't really need a dictionary, but need to cut down the count of queries, you can use this method like this:
posts = Post.objects.all().only('id', 'user', 'title').prefetch_related('tags')
It seems to execute only 2 queries (one for Post and another for Tag with INNER JOIN).
And then you can access post.tags.all without extra queries, because tags was already prefetched.
{% for post in posts %}
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
{% endfor %}
I'm working on a project where users can vote on collections of books. My models look like this:
class Collection(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=31)
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
collection = models.ForeignKey(Collection)
vote_choices = ((-1,'Bad'),
(0, 'Not good, Not bad'),
(1,'Good'))
class Vote(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, related_name='votes')
book = models.ForeignKey(Book)
vote = models.IntegerField(choices=vote_choices)
I need to display a view for the users where they can see all the books in a given collection and vote for those books at once, since this is much more user-friendly than looping over everybook to vote. So I need a formset where every form will be: a book and its vote.
Initially I thought this was a good opportunity to use formsets (I have never used them before, but I'm used to work with forms, modelforms), but I'm running into difficulties on how to create this formset.
I would like to use generic class based views, since, in theory, I could easily create/update all votes easily, but I can't figure out how to do this.
Right now, I don't have a direct relation between the Collection model and the User (from django.contrib.auth.model) model, but I don't mind adding one if this makes things easier. I don't really need one, since every User is linked to a Book through a Vote and every Book is linked to a Collection.
I'm not sure whether I should use formsets or just regular modelforms.
How would you do it? Thanks in advance for any help.
I would recommend not to use django forms for this case. There are some more convenient ways.
First way. Using pure jQuery
Extend your book class:
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
collection = models.ForeignKey(Collection, related_name='books')
def get_vote(self, user):
vote = Vote.objects.filter(book=self, user=user)
if vote:
return vote.vote
else:
return ''
Your views (you can rewrite it on CBV if you want):
def list_collection(request, collection_id):
collection = Collection.objects.get(pk=id)
user = request.user
books = []
for item in collection.books.all():
book = {
'id': item.pk,
'name': item.name,
'vote': item.get_vote(user)
}
books.append(book)
return render_to_response('colleciton.html', {'books': books})
def set_vote(request):
id = request.GET('id')
vote = int(request.GET('vote'))
user = request.user
book = Book.objects.get(pk=id)
vote = Vote()
vote.user = user
vote.vote = vote
vote.book = book
vote.save()
return HttpResponse()
Your urls:
url(r'^list-collection/', 'list_collection', name='list_collection'),
url(r'^set-vote/', 'set_vote', name='set_vote'),
Your django template:
{% extends 'base.html' %}
{% block content %}
{% for book in collection.books.all %}
<div class="book" data-id="{{ book.id }}">
<div class="book-name">{{ book.name]}</div>
<div class="book-vote" {% if not book.get_vote %}style="display:none"{% endif %}>book.get_vote</div>
<div class="voting" {% if book.get_vote %}style="display:none"{% endif %}>
<button class="vote-up"></button>
<button class="vote-down"></button>
</div>
</div>
{% endfor %}
{% endblock %}
And the javascript is like
$(document).ready(function(){
$('.vote-up, .vote-down').on('click', function(e){
var id, vote, t, book_el;
e.preventDefault();
t = $(e.currentTarget);
book_el = t.parents().parents()
id = book_el.attr('data-id');
if t.hasClass('vote-up'){
vote = 1;
} else {
vote = -1;
}
book_el.find('.book-vote').show();
book_el.find('.book-vote').text(vote);
book_el.find('voting').hide();
$.get("/set-vote", {vote: vote, id: id});
});
});
Second way. Using javascript frameworks like Backbone.js
It makes the whole process much easier. Especially if you plan to add some other features to your app.
From the other hand Backbone requires some time to learn it.
Read the docs, look at the tutorial app TodoMVC or other tutorials.
Maybe you'll find it more appropriate for your task.