I am implementing the following schema in a Django app, but I am new to Django's ORM:
Briefly, a DayMenu lists multiple MenuItems. (A MenuItem is simply a many-to-many relationship between a DayMenu and a Meal.) Each User selects a MenuItem from the DayMenu. (This choice is represented as a UserItemChoice.)
In our first draft models.py (below), MenuItem is defined as a many-to-many field on the DayMenu model.
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Meal(models.Model):
# field options: diet
MEAT = "MEAT"
VEGETARIAN = "VEGET"
HALAAL = "HALAA"
DIET_CHOICES = (
(MEAT, "Meat"),
(VEGETARIAN, "Vegetarian"),
(HALAAL, "Halaal"),
)
# field options: type
FREE = "FREE"
PAID = "PAID"
SKIP = "SKIP"
TYPE_CHOICES = (
(FREE, "Free"),
(PAID, "Paid"),
(SKIP, "Skip"),
)
# fields
cost = models.IntegerField(default=10)
description = models.CharField(max_length=120)
diet = models.CharField(max_length=5, choices=DIET_CHOICES)
type = models.CharField(max_length=5, choices=TYPE_CHOICES)
class DayMenu(models.Model):
# fields
date = models.DateField()
locked = models.BooleanField(default=False)
item = models.ManyToManyField(Meal) # TODO: confirm (replaces MenuItem in schema)
# class UserItemChoice(models.Model):
#
# # fields
# user = models.CharField() # FIXME
# menuitem = models.CharField() # FIXME
# selected = models.BooleanField(default=False)
# like = models.NullBooleanField(default=None)
How do we define UserItemChoice given that:
it is itself a many-to-many relationship
it links to a many-to-many field rather than a (explicit) model
it would (ideally?) be a many-to-many field on the built-in user table
I think what you want is to define UserItemChoice as a through model of m2m relationship between User and MenuItem. through model is mainly used when you want to define some extra attributes between the m2m relationship.
Here a user could have multiple MenuItems, but you would also want attributes like selected and like attributes coming with the relationship, but moving these 2 attributes to either model is no good, hence the through is the best solution.
Check out django doc about through definition and example.
Related
I have the following models in my models.py file in my django project
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings
class CustomUser(AbstractUser):
pass
# add additional fields in here
class PDFForm(models.Model):
pdf_type=models.IntegerField(default=0)
pdf_name=models.CharField(max_length=100,default='')
file_path=models.FileField(default='')
class FormField(models.Model):
fk_pdf_id=models.ForeignKey('PDFForm', on_delete=models.CASCADE,default=0)
field_type=models.IntegerField(default=0)
field_page_number=models.IntegerField(default=0)
field_x=models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_y=models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_x_increment=models.DecimalField(max_digits=6,decimal_places=2,default=0)
class Meta:
ordering= ("field_page_number", "field_type")
class UserData(models.Model):
fk_user_id=models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,default=0)
field_type=models.IntegerField(default=0)
field_text=models.CharField(max_length=200,default='')
field_date=models.DateField()
Here is how the models are related
1) a pdfform contains a pdf form and path for it on the file system
2) A pdfform has multiple FormFields in it. Each field has attributes, and the specific one under discussion is field_type
3)The UserData model has user's data, so one User can have multiple rows in this table. This model also has the field_type column.
What I am trying to query is to find out all rows present in the Userdata Model which are present in the FormField Model ( matched with field_type) and that are of a specific PDFForm.
Given that the Many to Many relationship in django models cannot happen between no unique fields, how would one go about making a query like below
select a.*, b.* from FormField a, UserData b where b.fk_user_id=1 and a.fk_pdf_id=3 and a.field_type=b.field_type
I have been going through the documentation with a fine toothed comb, but obviously have been missing how django creates joins. what is the way to make the above sql statement happen, so I get the required dataset?
I think UserData is missing a relation to FormField, but if you had this relation you could do:
UserData.objects.filter(
fk_user_id=1, # Rename this to user, Django wilt automicly create a user_id column
form_field__in=FormField.objects.filter(
fk_pdf_id=<your pdfid> # same as fk_user_id
)
)
Edit updated models
When you use a ForeignKey you don't have to specify the _id or default=0, if you don't always want to fill the field its better to set null=True and blank=True
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings
class CustomUser(AbstractUser):
pass
# add additional fields in here
class FieldTypeMixin:
TYPE_TEXT = 10
TYPE_DATE = 20
TYPE_CHOISES = [
(TYPE_TEXT, 'Text'),
(TYPE_DATE, 'Date'),
]
field_type=models.IntegerField(default=TYPE_TEXT, choises=TYPE_CHOISES)
class PDFForm(models.Model):
pdf_type = models.IntegerField(default=0)
pdf_name = models.CharField(max_length=100,default='')
file_path = models.FileField(default='')
class FormField(models.Model, FieldTypeMixin):
pdf_form = models.ForeignKey('PDFForm', on_delete=models.CASCADE)
field_page_number = models.IntegerField(default=0)
field_x = models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_y = models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_x_increment = models.DecimalField(max_digits=6,decimal_places=2,default=0)
class Meta:
ordering = ("field_page_number", "field_type")
class SubmittedForm(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE)
pdf_form = models.ForeignKey(PDFForm, models.CASCADE)
class SubmittedFormField(models.Model, FieldTypeMixin):
submitted_form = models.ForeignKey(SubmittedForm, models.CASCADE)
form_field = models.ForeignKey(FormField, models.CASCADE, related_name='fields')
field_text = models.CharField(max_length=200,default='')
field_date = models.DateField()
class Meta:
unique_together = [
['submitted_form', 'form_field']
]
I'm trying to simply create one to many relation model of categories using Django amazing ORM.
SQL:
create table categories(
id serial primary key not null,
parent_id int
);
insert into categories values(default,default,default);
update categories set parent_id = 1 where id > 1;
select * from categories;
id | parent_id
----+-----------
2 | 1
3 | 1
1 |
(3 rows)
Django amazing orm model:
class Categories(models.Model):
id = models.AutoField(primary_key=True)
parent_id = models.ForeignKey('self')
class Meta:
managed = False
db_table = 'categories'
Django Query:
Categories.objects.get(id=1)
OUTPUT:
django.db.utils.ProgrammingError: column categories.parent_id_id does not exist
LINE 1: SELECT "categories"."id", "categories"."parent_id_id" FROM "...
^
HINT: Perhaps you meant to reference the column "categories.parent_id".
Why it uses parent_id_id column instead of parent_id and how I can force it to use parent_id?
EDIT
I just changed parent_id field to parent.
EDIT 2
tatlar answer is not in my case becouse i already have database schema.
So after digging more deeper in documentation and other questions on stackoverflow there is what i have in result. This model contains reference to parent and children categories for each row. It could be inherited for all graph alike data models (comments, categories etc).
class Categories(models.Model):
id = models.AutoField(primary_key=True)
parent = models.ForeignKey('self', on_delete=None, parent_link=True, related_name='children')
class Meta:
managed = False
db_table = 'categories'
Get all children for category 1:
from myapp.models import Categories
ch = Categories.objects.get(id=1).children print (ch)
# <QuerySet [<Categories: Categories object (2)>, <Categories: Categories object (3)>]>
Get parent for category 2:
from myapp.models import Categories
ch = Categories.objects.get(id=1).parent
print (ch)
# <Categories: Categories object (1)>
Sorry to hear that you are having trouble with Django. In time you may grow to love the Django ORM and how it abstracts all the SQL code for you :)
You need to dig a little deeper into how the ORM works -- it's not a 1:1 replacement for SQL code. Check out the Model docs.
In your specific case, you need to create a new class called Parent and reference that class (via a ForeignKey) from your Categories class (you might also like to rename your Categories class to Category -- the ORM also handles plurals).
Try the code below (where I have already renamed Categories to Category for you):
from django.db import models
class Category(models.Model):
id = models.AutoField(primary_key=True)
parent = models.ForeignKey(Parent)
# ... Extra model attributes
class Meta:
verbose_name_plural = "categories"
class Parent(models.Model):
id = models.AutoField(primary_key=True)
# ... Extra model attributes
Then add all the extra attributes you need. This will create all the database tables, and their relationships, without you ever writing any SQL. If you are used to writing SQL it is a change, but it makes sense as you work more with the ORM and understand how good it is actually architected.
Good luck!
tatlar answer is not in my case becouse i already have database schema.
So after digging more deeper in documentation and other questions on stackoverflow there is what i have in result. This model contains reference to parent and children categories for each row. It could be inherited for all graph alike data models (comments, categories etc).
class Categories(models.Model):
id = models.AutoField(primary_key=True)
parent = models.ForeignKey('self', on_delete=None, parent_link=True, related_name='children')
class Meta:
managed = False
db_table = 'categories'
Get all children for category 1:
from myapp.models import Categories
ch = Categories.objects.get(id=1).children
print (ch)
# <QuerySet [<Categories: Categories object (2)>, <Categories: Categories object (3)>]>
Get parent for category 2:
from myapp.models import Categories
ch = Categories.objects.get(id=1).parent
print (ch)
# <Categories: Categories object (1)>
I have 2 models:
1: KW (individual keywords)
2: Project (many keywords can belong to many different projects)
class KW(models.Model):
...
project = models.ManyToManyField('KWproject', blank=True)
class KWproject(models.Model):
ProjectKW = models.CharField('Main Keyword', max_length=1000)
author = models.ForeignKey(User, editable=False)
Now when user is in Admin for KWproject they should be able to see all keywords belonging to selected project in list_display. I achieved this but it doesn't feel like proper way.
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
list_display = ('Keywordd', 'author')
def Keywordd(self, obj):
return '%s' % (obj.id, obj.ProjectKW)
Keywordd.allow_tags = True
Keywordd.admin_order_field = 'ProjectKW'
Keywordd.short_description = 'ProjectKW'
Is there better way to link and then list_display all items that have reverse relationship to the model? (via "project" field in my example)
As per the Django Admin docs:
ManyToManyField fields aren’t supported, because that would entail
executing a separate SQL statement for each row in the table. If you
want to do this nonetheless, give your model a custom method, and add
that method’s name to list_display. (See below for more on custom
methods in list_display.)
So, you may opt to implement a custom model method like so:
# models.py
class KW(models.Model):
...
project = models.ManyToManyField('KWproject', blank=True)
class KWproject(models.Model):
ProjectKW = models.CharField('Main Keyword', max_length=1000)
author = models.ForeignKey(User, editable=False)
def all_keywords(self):
# Retrieve your keywords
# KW_set here is the default related name. You can set that in your model definitions.
keywords = self.KW_set.values_list('desired_fieldname', flat=True)
# Do some transformation here
desired_output = ','.join(keywords)
# Return value (in example, csv of keywords)
return desired_output
And then, add that model method to your list_display tuple in your ModelAdmin.
# admin.py
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
list_display = ('Keywordd', 'author', 'all_keywords')
def Keywordd(self, obj):
return '%s' % (obj.id, obj.ProjectKW)
Keywordd.allow_tags = True
Keywordd.admin_order_field = 'ProjectKW'
Keywordd.short_description = 'ProjectKW'
Do take note: This can potentially be a VERY EXPENSIVE operation. If you are showing 200 rows in the list, then a request to the page will execute 200 additional SQL queries.
The following shows up instead of a field in my template.
<django.contrib.localflavor.us.forms.USStateSelect object at 0x92b136c>
my template has
{{ form.state }}
what could the issue be?
class RegistrationForm(forms.Form):
first_name = forms.CharField(max_length=20)
last_name = forms.CharField(max_length=20)
phone = USPhoneNumberField()
address1 = forms.CharField(max_length=45)
address2 = forms.CharField(max_length=45)
city = forms.CharField(max_length=50)
state = USStateSelect()
zip = USZipCodeField()
also is there anyway i can make the state and zip optional?
To limit the choices to a drop down list, use us.us_states.STATE_CHOICES in your model, and use us.forms.USStateField() instead of us.forms.USStateSelect() in your forms.
To make a field optional in a form, add blank = True to that field in the model.
from django.contrib.localflavor.us.us_states import STATE_CHOICES
from django.contrib.localflavor.us.models import USStateField
class ExampleLocation(models.Model):
address1 = models.CharField(max_length=45) #this is not optional in a form
address2 = models.CharField(max_length=45, blank = True) #this is made optional
state = USStateField(choices = STATE_CHOICES)
Instead of STATE_CHOICES, there are several options you can find in the localflavor documentation. STATE_CHOICES is the most inclusive, but that may not be what you desire. If you just want 50 states, plus DC, use US_STATES.
This answer assumes you're using ModelForms. If you aren't, you should be. Once you've made your model, you should follow DRY and create basic forms like so:
from django.forms import ModelForm
class ExampleForm(ModelForm):
class Meta:
model = ExampleLocation
And it inherits your fields from your model. You can customize what fields are available, if you don't want the whole model, with other class Meta options like fields or exclude. Model forms are just as customizable as any other form, they just start with the assumption of your model's fields.
I am very new to Django and would appreciate your help with this problem:
I have a table with meta-informationen (like the number of clicks, votes, comments ...) for different areas of my website (news, events in the calendar, films ..). The table is referenced by two primary keys (fi = INTEGER and tbl = CHAR).
class News(models.Model):
title = models.CharField()
...
class Film(models.Model):
title = models.Charfield()
...
class Calendar(models.Model):
title = models.Charfield()
...
class MetaInfo(models.Model):
fi = ForeignKey(??) # Integer
tbl = ForeignKey(??) # CharField
Example: fi = 1 and tbl = 'news' would relate to news-entry with primary key 1. And fi = 100, tbl = 'film' would relate to film-entry with primary key 100.
How to implment this? Is this even possible with django?
Django has a built-in feature called Generic Foreign Keys that allow you to tie a single table to multiple models in this fashion.
This is how you would create your models:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class News(models.Model):
title = models.CharField()
...
class Film(models.Model):
title = models.Charfield()
...
class Calendar(models.Model):
title = models.Charfield()
...
class MetaInfo(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
If you wish, you can be more explicit about the relationship between a model and MetaInfo by using GenericRelation. For example:
class Film(models.Model):
title = models.CharField()
metainfo = generic.GenericRelation('MetaInfo')
...
This allows you to access the related MetaInfo records directly from the Film model, as in
f = Film.objects.get(pk=1)
for mi in f.metainfo.all():
#mi is a matching MetaInfo record for Film record with pk=1
Just to elaborate a bit:
In the MetaInfo model, content_type serves as the equivalent of your tbl column (although it points to a Django construct called a ContentType; Django constructs one for each model in the app/set of apps) and object_id corresponds to your fi key. You actually generally don't pay much attention to those fields. Instead, you get and set the content object, which is the corresponding record. For example, instead of storing or retrieving tlb='Film', fi=1, you'd get or set content_object which corresponds directly to the Film record matching pk=1.
In essence, contenttype__name='Film', object_id=1 while content_object=Film.object.get(pk=1)
This is all assuming that this database is for Django use only. If it's an existing database that you're trying to use within Django, there isn't a straightforward way to handle this that I'm aware of.