I'm building a project that involves a list of people and the transactions they've made. Each person will have their own profile with x amount of transactions.
My database, so far, consists of two models:
One to define a person
class Person (models.Model):
"""
A person
"""
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100, blank=True)
def __unicode__(self):
return self.name
One to associate a transaction each person made.
class Purchase (models.Model):
"""
A purchase for each person
"""
person_id = models.ForeignKey(Person)
purchase_description = models.CharField(max_length=1000, blank=True)
I determined this would be a many-to-one database relationship because some people might make the same transaction.
Now, I can make pages for each person, showing their name in the header. What I'd like to do is show the transactions made to that particular person. How can this be done? Here's what my view.py file looks like:
from django.shortcuts import render
from .models import Person, Purchase
def load_person(request, name):
person = Person.objects.get(name=name)
# name = Purchase.objects.filter(purchase)
context = {
'name': name
# 'purchase': purchase
}
return render(request, 'pages/person.html', context)
Here's the url associated to the query in my url.py file.
url(r'^project/(?P<name>[-\w]+)/$', views.load_person),
person = Person.objects.get(name=name)
purchases = Purchase.objects.filter(person_id=person)
Sounds like you just started using django, there are several things that are not best practice, they are:
You don't need to define id in Person, django will define it for you.
Try not to use person_id as field name, use person instead. Django model is not relational database design, think of the models as separate entities.
Related
My problem is as follows. I am saving data for patients from a form on a webpage. The form is generated from model definitions in models.py. The information that I save is name, surname amongst others. I have a field for diagnosis which is selected using a multichoiceField and I save it using manytomany.
When the data is saved, a separate table is created for the diagnosis assigned to each patient as expected. The table contains a diagnosis and the ID of the patient it applies to. Each diagnosis is saved as a separate record.
In addition to selecting the diagnosis, I also save the date that the diagnosis is made. You will see what I mean in the models.py and form.py code below.
I would like to have the date for which the diagnosis was made also saved in the table but I can't figure out how to do this. I have tried following the docs: https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany as well as some other posts on SO, but cannot figure out how to do it. I can't figure out how the views, forms and models need to be set up in order to achieve. Is it possible to do this and if so how? I have tried using an intermediate model with manytomany and 'through', but I do not understand it. Any help with this would be greatly appreciated.
Below is a simplified version of my code:
models.py:
class diagnosisChoices(models.Model): #This represents the list in the drop down menu for the different diagnosis.
diagnosis = models.CharField(max_length = 50)
def __str__(self):
return self.diagnosis
class PatientData(models.Model):
Name = models.CharField(max_length=100)
Surname = models.CharField(max_length=100)
dateOfBirth = models.DateField(default = datetime.datetime.now())
diagnosis = models.ManyToManyField(
'diagnosisChoices',
#on_delete=models.CASCADE,
)
views.py:
def patientDataView(request):
uId = request.user.id
if request.method == "POST":
form = PatientDataForm(request.POST)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.timestamp = timezone.now()
model_instance.save()
#model_instance.add(uId)
form.save_m2m()
return HttpResponseRedirect('/dataBase')
else:
form = PatientDataForm()
return render(request, "dataBaseTest.html", {'form': form})
date_of_diagnosis = models.DateField(default=datetime.datetime.now())
forms.py
from django.forms import ModelForm
from django import forms
from .models import PatientData
from .models import diagnosisChoices #This is the list of diagnosis in the dropdown
from django.forms import extras
import datetime
from functools import partial
class PatientDataForm(ModelForm):
class Meta:
now = datetime.datetime.now()
thisYear = now.year
DateInput = partial(forms.DateInput, {'class': 'datepicker'})
widgets = {
}
model = PatientData
fields = ['Name',
'Surname',
'dateOfBirth',
'diagnosis',
'date_of_diagnosis',
]
Thanks,
Thomas
The main thing that you are not getting is on the models.py, so I will focus on it.
You need three tables to do what you have described: diagnosisData, PatientData and a 'membership' table which I call diagnosisPatient. Then you build your model like this:
class diagnosisChoices(models.Model):
diagnosis = models.CharField(max_length = 50)
class PatientData(models.Model):
Name = models.CharField(max_length=100)
Surname = models.CharField(max_length=100)
dateOfBirth = models.DateField(default = datetime.datetime.now())
diagnosis = models.ManyToManyField('diagnosisChoices',through='diagnosisPatient')
class diagnosisPatient(models.Model):
patient = models.ForeignKey('PatientData')
diagnosis = models.ForeignKey('diagnosisChoices')
dateOfDiagnosis = models.DateField()
Once you have your model built this way, you should save your PatientData and your diagnosisChoices instances as usual. FOr the many to many relation, you should save it manualy on the diagnosisPatient table using the apropriate foreign keys and date. You can query the many to many relation from the PatientData model as usual with objects.all() function.
The thing here to keep in mind is that ManyToMany relations in django are always creating a new membership table for you behind the scenes. So when you do not need to insert extra information on the relationship the diagnosisPatient table is just made of two foreign keys, and it is hidden. The through argument on this relationship is just bringing this table to light and giving you control back to put whatever new relationship you like.
I'm new to Django so I make 3 simple tables to return a WishList. The thing is that I want whenever user asks for WishList, his/her user_id is used to make a SELECT query to return his/her own WishList. And I want to get product title and product url from my WishList table. I'm using to_field but with that way I only can get product title back. I don't know much about Django so help me!
Product
class Product(models.Model):
class Meta:
unique_together = (('id', 'title'),)
title = models.CharField(max_length=200, unique=True,
help_text='Name of the product')
url = models.CharField(max_length=300, default='',
help_text='Url of the product')
def __str__(self):
return 'Product: {}'.format(self.title)
WishList
class WishList(models.Model):
class Meta:
unique_together = (('user', 'product'),)
user = models.ForeignKey(fbuser,
on_delete=models.CASCADE,
help_text='Facebook user',
to_field='user_id')
product = models.ForeignKey(Product, to_field='title', db_column='title',
on_delete=models.CASCADE)
def __str__(self):
return 'WishList: {}'.format(self.user)
It's not a good practice to override to_field to another field different than your model.pk unless you have a really good reason and you know what you are doing (definitely not the case right now).
So after you read the docs, you will know that in order to get wishlisht related to a user, you can use the ForeignKey reverse relation to get all related wishlists for a user.
user_wishlists = my_user.wishlist_set.all()
#Because we know that you want to access the wishlist.products
#in order to optimize things (in terms of db queries)
#you can add and .select_related('product')
#e.g, user_wishlists = my_user.wishlist_set.all().select_related('product')
#now follow the wishlist.product foreign key to access the related product for every wishlist
for wishlist in user_wishlists:
product = wishlist.product
print (product.id, product.title, product.url)
Now after you read a little bit more of the documentation
you will notice that your WishList model is in fact an intermediate model for a ManyToMany relation between User and his wished products, then you will know that you can define a M2M field between user and products via WishList like so:
class FbUser(models.Model):
#...
wished_products = models.ManyToManyField(
Product,
through='WishList',
through_fields=('user', 'product')
)
#and now accessing user wished products would be easy as:
user_wished_products = my_user.wished_products.all()
for product in user_wished_products:
print (product.id, product.title, product.url)
I have a m2m relationship between Servers and Products in Django with a through table called ServerProducts.
class ServerProduct(TimeStampedModel):
# Additional fields may be required in the future
server = models.ForeignKey('Server', on_delete=models.CASCADE)
product = models.ForeignKey('Product', on_delete=models.CASCADE)
class Server(TimeStampedModel):
name = models.CharField(max_length=35)
# ...
products = models.ManyToManyField('Product', through='ServerProduct',
related_name='products', blank=True)
class Product(TimeStampedModel):
name = models.CharField(max_length=45, unique=True)
# ...
servers = models.ManyToManyField(
'Server', through='ServerProduct', related_name='servers')
In my view I have a form which allows users to create a Server and select from a list of all products for the Server to be associted with.
In order to create the ServerProduct objects (for the through table) on each save I have to write the following code inside save().
class ServerForm(forms.ModelForm):
class Meta:
model = Server
fields = '__all__'
def save(self, commit=True):
instance = super(ServerForm, self).save(commit=False)
instance.save()
if instance.products.count():
instance.products.clear()
for product in self.cleaned_data['products']:
ServerProduct.objects.create(server=instance, product=product)
return instance
I want to be able to reuse the form for both Create and Update views. Hence why I have to check if the current Server is associated with any products, and then do instance.products.clear(). To make sure it removes any previous products if they get deselected by a user.
This entire process feels unecessary, especially when I've read a lot about Django's built-in form.save_m2m() method. My question is, is there a simpler way do achieve what I'm doing using Django built-in's?
Suppose I have these models:
class House(models.Model):
name = models.CharField(max_length=50)
# Other attributes
class Booking(models.Model):
house = models.ForeignKey(House, on_delete=models.CASCADE)
arrival_date = models.DateField()
departure_date = models.DateField()
Serializers:
class HouseSerializer(serializers.ModelSerializer):
class Meta:
model = House
fields = ('id','name')
class BookingSerializer(serializers.ModelSerializer):
class Meta:
model = Booking
fields = ('id',
'arrival_date',
'departure_date',
'house')
As you can see, bookings are associated to houses.
Users can request information about a house through "/house/:houseId", and bookings through "/booking/:bookingId".
I'd like to return all bookings related to a house when a user requests "/house/bookings", but these should NOT be returned when simply requesting "/house/bookings" as this is a relatively expensive operation and not usually needed.
I know how to make them be returned with the house, but how to make this optional. How do I do that?
First, it makes more sense (to me at least) to have your endpoint for bookings for a given house exist at /house/:houseId/bookings/ since the namespace at /house/bookings/ will already be looking for an ID.
You can also serialize the relationship between a House and Booking to display bookings for a house at the house detail endpoint. Something like:
class HouseSerializer(serializers.ModelSerializer):
bookings = BookingSerializer(many=True)
class Meta:
model = House
fields = ('id','name', 'bookings',)
But if you want another endpoint, just create a view that takes the BookingSerializer and filters the queryset by the House ID in the kwargs:
(Again, assuming your endpoint is /house/<house_id>/bookings/)
class BookingsForHouseListView(ListAPIView):
serializer_class = BookingSerializer
def get_queryset(self):
return Bookings.objects.filter(house__id=self.kwargs['house_id'])
Maybe you can do two serializers, one to the List of houses without showing the books, and another for the individual house, showing the books.
In the views you define the list serializer for the list uri, and the individual serializer for the individual uri.
Turns out, I was trying with too high-level parts of the API that restricted me more than they were helping me, I needed to do some things manually.
This answer https://stackoverflow.com/a/17763864/1582024 shows how to implement a hierarchy of resources.
I have a blog app that consists of 3 models: department, author, post
I am having trouble structuring the models correctly and creating the corresponding forms
models.py
from django.db import models
class Department(models.Model):
name=models.CharField(max_length=20)
posts = models.ForeignKey('Post')
authors = models.ManyToManyField('Author')
def __unicode__(self):
return self.name
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
post = models.ForeignKey('Post')
def __unicode__(self):
return self.last_name
class Post(models.Model):
title=models.CharField(max_length=20)
post = models.TextField()
creation_date = models.DateField(auto_now_add=True)
def __unicode__(self):
return self.title
The idea is that a department can have many posts, but each post belongs to only one department. A department can also be made up of multiple authors and authors can be in multiple departments. Where I'm really having trouble is with the forms.
The relevant urls.py looks like this:
url(r'^(?P<department_id>\d+)/posts/$', views.posts, name='posts'),
url(r'^(?P<department_id>\d+)/add_post/$', views.add_post, name="add_post"),
So I can pull in all the posts by department. The goal of the form is for the department id to be recognized and added automatically to the post.
def add_post(request, department_id):
department = Department.objects.get(pk=department_id)
if request.method == 'POST':
new_post_form = PostForm(data=request.POST)
if new_post_form.is_valid():
new_post = new_post_form.save(commit=False)
new_post.department = department
new_post.save()
return redirect('posts', department_id=department_id)
Now I realize that the Post model does not have a department attribute, which is the error that I get, but I'm guessing that there's a way to make this happen, I just don't know what it is.
Thanks as always for your help. Please let me know if anything is unclear.
The fact that the Post model does not have a department attribute should have given you the clue that your structure is wrong: it clearly needs one. The issue is that you have your ForeignKey the wrong way round: a FK is a one-to-many relationship, and lives on the "many" side, in your case Post, pointing to the "one", ie the Department.
Then your view code will work exactly as it is, and you can retrieve all posts for a department with my_department.post_set.all().