I'm using django to create a signup platform where students can signup to weekly classes.
Each class is a django model called ClassCards which has a ManytoMany relation to User model called signed_up_student that represents all the users signed up for that class as seen below
class ClassCards(models.Model):
content = models.CharField(max_length=100, default = '')
date = models.DateField(blank=True, null = True)
time = models.TimeField(blank=True,null=True)
signed_up_students = models.ManyToManyField(User,blank=True)
full = models.BooleanField(default = False)
max_students = models.IntegerField(default=4)
teacher = models.CharField(max_length=100, default='Adi')
I would like to add a subscription option that will automatically sign up subscribed students to this weeks class. Here is my Subscription model:
class Subscriptions(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null =True)
day = models.CharField(max_length=100, choices=day_choices, null=True)
time = models.TimeField(blank=True, null=True)
num_of_times_used = models.IntegerField(default=0)
cap = models.IntegerField(default=52)
active = models.BooleanField(default= True)
expirey_date = models.DateField()
date_created = models.DateField(default = timezone.now)
To accomplish this I have created a post_save signal:
#receiver(post_save,sender=ClassCards)
def apply_subsciptions(sender,instance,created, **kwargs):
if created:
subs = Subscriptions.objects.filter(day=instance.date.strftime("%A"),
time=instance.time)
for s in subs:
instance.signed_up_students.add(s.user)
print(instance.signed_up_students.get())
The code runs properly when a ClassCards is saved without throwing any errors and the print statement prints the relevant User However when I look on the admin page, I see that there are no users in the signed_up_students field.
I Would like to understand why this isn't working as desired which should adding that user to the ManytoMany field and what is the best practice for automatically updated a ManytoMany fields.
a little modification to the class ClassCards
class ClassCards(models.Model):
signed_up_students = models.ManyToManyField(User, symmetrical=False, related_name="student_cards_students_has", blank=True)
def signed_students_list(self):
return self.signed_up_students.all()
def cards_asigned_to_student_list(self):
return self.student_cards_students_has.all()
def assign_student(self, user):
self.signed_up_students.add(user)
def unsign_user(self, user):
self.signed_up_students.remove(user)
now in the signals
#receiver(post_save,sender=ClassCards)
def apply_subsciptions(sender,instance,created, **kwargs):
if created:
subs = Subscriptions.objects.filter(day=instance.date.strftime("%A"),time=instance.time)
for s in subs:
instance.assign_student(s.user)
instance.save()
print(instance.signed_students_list())
print(instance.cards_asigned_to_student_list())
What is the deal: I'm crating a site where different types of objects will be evaluated, like restaurants, beautysalons, car services (and much more).
At the beginning I start with one app with with Polymorfic Model:
models.py:
from django.db import models
from users.models import ProfileUser
from django.utils import timezone
from polymorphic.models import PolymorphicModel
class Object(PolymorphicModel):
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
city = models.ForeignKey(City, on_delete=models.CASCADE)
address = models.CharField(max_length=300)
phone = models.CharField(max_length=20, default='')
email = models.CharField(max_length=100, default='')
site = models.CharField(max_length=100, default='')
facebook = models.CharField(max_length=100, default='')
instagram = models.CharField(max_length=100, default='')
content = models.TextField()
rating = models.DecimalField(default=10.0, max_digits=5, decimal_places=2)
created_date = models.DateTimeField(default=timezone.now)
approved_object = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
def __str__(self):
return f"{self.title}"
class Restaurant(Object):
seats = models.IntegerField()
bulgarian_kitchen = models.BooleanField(default=False)
italian_kitchen = models.BooleanField(default=False)
french_kitchen = models.BooleanField(default=False)
sea_food = models.BooleanField(default=False)
is_cash = models.BooleanField(default=False)
is_bank_card = models.BooleanField(default=False)
is_wi_fi = models.BooleanField(default=False)
category_en_name = models.CharField(max_length=100, default='restaurants')
category_bg_name = models.CharField(max_length=100, default='Ресторанти')
bg_name = models.CharField(max_length=100, default='Ресторант')
is_garden = models.BooleanField(default=False)
is_playground = models.BooleanField(default=False)
class SportFitness(Object):
is_fitness_trainer = models.BooleanField(default=False)
category_en_name = models.CharField(max_length=100, default='sportfitness')
category_bg_name = models.CharField(max_length=100, default='Спорт и фитнес')
bg_name = models.CharField(max_length=100, default='Спорт и фитнес')
class CarService(Object):
is_parts_clients = models.BooleanField(default=False)
category_en_name = models.CharField(max_length=100, default='carservice')
category_bg_name = models.CharField(max_length=100, default='Автосервизи')
bg_name = models.CharField(max_length=100, default='Автосервиз')
class Comment(models.Model):
object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
content = models.TextField()
rating = models.TextField()
approved_object = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.content}"
class Images(models.Model):
object = models.ForeignKey(Object, default=None, on_delete=models.CASCADE)
image = models.ImageField(upload_to='attachments',
verbose_name='Image')
class ObjectCoordinates(models.Model):
object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name='coordinates')
latitude = models.CharField(max_length=60)
longitude = models.CharField(max_length=60)
Don't mention that name Object is wrong, I already know that :)
So all logic about different objects was in one App and this start to cause some problems, like:
views.py:
def show_object(request, category, pk, page_num):
categories = {'restaurants' : 'Restaurant', 'sportfitness' : 'SportFitness', 'carservice' : 'CarService'} # probably this is not good way to do it
obj = apps.get_model('objects', categories[category]).objects.get(id=pk)
def show_all_objects(request, category, page_num, city=None):
params_map = {
'restaurants': Restaurant,
'sportfitness': SportFitness,
'carservice': CarService,
}
objects = Object.objects.instance_of(params_map.get(category))
and other problems in templates (a lot of if-else blocks) etc.
So I decide to change whole structure and put every model in different app, so now I have app:restaurants, app:sportfitness, app:carservices, etc. But it begin to cause some problems, again, like this model:
class ObjectCoordinates(models.Model):
object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name='coordinates')
latitude = models.CharField(max_length=60)
longitude = models.CharField(max_length=60)
All of objects (restaurants, car services) has coordinates of map, so I'm not sure how to handle it, with Model ObjectCoordinates . If I create ObjectCoordinates for each of them, respectively a table in BD (then I will have some tables with different names but same structure, which is not very good, because except ObjectCoordinates, models share and other common models like Images and others, so at the end I will have a lot of tables with different names and same structure). Probably I should add one more column for object category, if I got two rows with same id of objects?
Probably change ObjectCoordinates and other common models to ManyToMany relation will prevent identical tables, but I'm not quite sure about that. Other problem is that there is a lot of repeated code (in views, templates). Also, now, I don't know how to get all objects (restaurants, car services) when they do not have common point, like Object model in first scenario with Polymorphic Model. Or I should keep different apps but to create common Model for all objects, and all of them to to inherit it.
Questions:
What structure is better, first one or second one?
What is the best wayt to implement such site (model structure)?
Should I create common point (model) for all models who they will inherit?
Here is my third attempt (notice that Object is renamed to Venue):
from django.db import models
from users.models import ProfileUser
from django.utils import timezone
from polymorphic.models import PolymorphicModel
# Create your models here.
class City(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return f"{self.name}"
class Category(models.Model):
name = models.CharField(max_length=20)
bg_name = models.CharField(max_length=20, default=None)
category_bg_name = models.CharField(max_length=100, default=None)
def __str__(self):
return f"{self.name}"
class Venue(models.Model):
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
city = models.ForeignKey(City, on_delete=models.CASCADE)
address = models.CharField(max_length=300)
phone = models.CharField(max_length=20, default='')
email = models.CharField(max_length=100, default='')
site = models.CharField(max_length=100, default='')
facebook = models.CharField(max_length=100, default='')
instagram = models.CharField(max_length=100, default='')
content = models.TextField()
rating = models.DecimalField(default=10.0, max_digits=5, decimal_places=2)
created_date = models.DateTimeField(default=timezone.now)
approved_venue = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
venue_category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='category')
def __str__(self):
return f"{self.title}"
class VenueFeatures:
seats = models.IntegerField()
bulgarian_kitchen = models.BooleanField(default=False)
italian_kitchen = models.BooleanField(default=False)
french_kitchen = models.BooleanField(default=False)
sea_food = models.BooleanField(default=False)
is_cash = models.BooleanField(default=False)
is_bank_card = models.BooleanField(default=False)
is_wi_fi = models.BooleanField(default=False)
is_garden = models.BooleanField(default=False)
is_playground = models.BooleanField(default=False)
is_fitness_trainer = models.BooleanField(default=False)
is_parts_clients = models.BooleanField(default=False)
is_hair_salon = models.BooleanField(default=False)
is_laser_epilation = models.BooleanField(default=False)
is_pizza = models.BooleanField(default=False)
is_duner = models.BooleanField(default=False)
is_seats = models.BooleanField(default=False)
is_external_cleaning = models.BooleanField(default=False)
is_internal_cleaning = models.BooleanField(default=False)
is_engine_cleaning = models.BooleanField(default=False)
is_working_weekend = models.BooleanField(default=False)
is_kids_suitable = models.BooleanField(default=False)
is_working_weekend = models.BooleanField(default=False)
venue = models.ForeignKey(Venue, on_delete=models.CASCADE, related_name='venue')
class Comment(models.Model):
venue = models.ForeignKey(Venue, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
content = models.TextField()
rating = models.TextField()
approved_venue = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.content}"
class Images(models.Model):
venue = models.ForeignKey(Venue, default=None, on_delete=models.CASCADE)
image = models.ImageField(upload_to='attachments',
verbose_name='Image')
class VenueCoordinates(models.Model):
venue = models.ForeignKey(Venue, on_delete=models.CASCADE, related_name='coordinates')
latitude = models.CharField(max_length=60)
longitude = models.CharField(max_length=60)
Now I do not now how to use Venue with VenueFeatures
Notice that features are just true/false values (checkboxes in form).
Okay, this is probably the best way to abstract anything as much as I can:
from django.db import models
from users.models import ProfileUser
from django.utils import timezone
from polymorphic.models import PolymorphicModel
# Create your models here.
class City(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return f"{self.name}"
class Category(models.Model):
name = models.CharField(max_length=20)
bg_name = models.CharField(max_length=20, default=None)
category_bg_name = models.CharField(max_length=100, default=None)
icon = models.CharField(max_length=40, default=None)
def __str__(self):
return f"{self.name}"
class Venue(models.Model):
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
city = models.ForeignKey(City, on_delete=models.CASCADE)
address = models.CharField(max_length=300)
phone = models.CharField(max_length=20, default='')
email = models.CharField(max_length=100, default='')
site = models.CharField(max_length=100, default='')
facebook = models.CharField(max_length=100, default='')
instagram = models.CharField(max_length=100, default='')
content = models.TextField()
rating = models.DecimalField(default=10.0, max_digits=5, decimal_places=2)
created_date = models.DateTimeField(default=timezone.now)
approved_venue = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return f"{self.title}"
class Feature(models.Model):
name = models.CharField(max_length=100)
code = models.CharField(max_length=100 )
category = models.ForeignKey(Category, on_delete=models.CASCADE)
type = models.CharField(max_length=100)
def __str__(self):
return f"{self.name}"
class VenueFeatures(models.Model): # ManyToMany Venues <-> Features
venue = models.ForeignKey(Venue, on_delete=models.CASCADE)
feature = models.ForeignKey(Feature, on_delete=models.CASCADE)
value = models.CharField(max_length=255)
class Comment(models.Model):
venue = models.ForeignKey(Venue, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
content = models.TextField()
rating = models.TextField()
approved_venue = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.content}"
class Images(models.Model):
venue = models.ForeignKey(Venue, default=None, on_delete=models.CASCADE)
image = models.ImageField(upload_to='attachments',
verbose_name='Image')
class VenueCoordinates(models.Model):
venue = models.ForeignKey(Venue, on_delete=models.CASCADE, related_name='coordinates')
latitude = models.CharField(max_length=60)
longitude = models.CharField(max_length=60)
Now Features are bound with Categories
Also Venues are ManyToMany with Features
I have already linked it to business logic and it works fine.
TL;DR Use a JSONField (JSONB automatically I think) in PostgreSQL WITHOUT a GIN index for your VenueFeatures instead of creating an entirely new model. Postgres has come a long way towards NoSQL/unstructured DB and it's really good. Using a JSONField in your Venue model would work really well. At the very bottom, I talk about how I would design your site's db.
Although I hate saying this, but this could be the job of a NoSQL database. Usually every application uses RDBM which is structured, but you are using unstructured attributes. You could try using PostgreSQL's JSONB field but... stuffing everything into one field would be tiresome for the GIN index + caching.
For now, I'll ignore a lot of weird practices such as needing to partition a couple of attributes, max_length for char field is typically 255 length for all databases, making sure the most accessed tables don't have too many attributes so that caching is better (i.e. you don't have to invalidate your cache every time a user updates your table), GeoDjango for your coordinate system with the standard Mercator projection system on Postgres Geography mode, and you could use sets instead of dicts (sets are iterables and use {} but nothing is repeated)...
Stay away from this option: For one, I NEVER recommend MongoDB, but it could be useful for you... so long as your application doesn't grow too large as in a couple million records could break your system.
The other RECOMMENDED option is PostgreSQL's JSONB or Django's JSONField withOUT a GIN index (I strongly recommend you don't index this field since venues could change them sooo often to the point that REINDEXING and caching would burn your server and slow your app). It can be useful to store a venue's "Features" inside of this JSONB field since everything is super unstructured.
Lowering the number of attributes is better. You've got A LOT of them too which could slow down querying. I recommend you use Django-cachalot for caching since they support JSONField which can avoid your issue of having a LOT of attributes.
Other recommendations in general
Instead of using default='', just do blank=True, null=True since you're basically saying the user doesn't have to fill out the email field.
Kind of like how you would have a user profile instead of stuffing ALL of your attributes inside of the main User model, you want to partition your Venue data into different models.
The way I would've designed this:
Since you originally had these three venues, just make the "Categories" table into choices.
from django.contrib.gis.db import models # This also imports standard models
from django.contrib.postgres.fields import JSONField # Remember to turn on GeoDjango with PostgreSQL's PostGIS extension
from django.contrib.postgres.indexes import BrinIndex
class Venue(models.Model):
id = models.BigAutoField(primary_key=True)
title = models.CharField(max_length=255)
rating = models.DecimalField(default=10.0, max_digits=5, decimal_places=2)
created_date = models.DateTimeField(default=timezone.now)
approved_venue = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
VENUE_TYPES = [
(1, "restaurant"),
(2, "concert"),
(3, "art night")
]
category = SmallPositiveIntegerField(choices=VENUE_TYPES)
location = models.PointField(srid=4326) # mercator projection from GeoDjango. You don't have to use this; you can stick to your old city and address thing
class Meta:
indexes = (
BrinIndex(fields=['category']), # this is in case you have a LOT of categories later on.
)
class VenueProfile(models.Model):
venue = models.OneToOneField(Venue, on_delete=models.CASCADE, primary_key=True)
misc_features = JSONField() # This field is for stuff like your restaurant features OR your concert features. You can put whatever you want in there. Just make sure you have a list of features that people have when trying to access the JSON so you don't run into exceptions.
created_date = models.DateTimeField(auto_now_add=True)
facebook = models.CharField(max_length=100, blank=True, null=True)
instagram = models.CharField(max_length=100, blank=True, null=True)
city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True) # SET_NULL in case you accidentally delete a city. You don't want to also delete the venue.
image = models.ImageField(upload_to='attachments',
verbose_name='Image')
# These attributes are universal for ANY venue so that's why they don't need to be in the JSONField
"""
For the rest of the features, I have no concern EXCEPT for city. Because you're using GeoDjango, you should also use MaxMind's free city database to determine location based on coordinates. That way, you've essentially scraped the need to store the user and such. You could probably save the address field since it could make things easier that a simple coordinate. It's really up to you. You could also use both!
"""
The attributes I've added to the Venue model are THE MOST important things in my opinion that a user would immediately want to know about.
The VenueFeature model is something that isn't updated that much. It's PRIME for using Django-cachalot to take over since it's not modified that often. (50 modifications per second makes invalidation of caches per modification a big hassle).
Comments model is fine.
I have users who listed their textbook.
I need to count objects in Textbook model and display total count in the side menu.
Here is my Model
from django.db import models
from django.http import HttpResponse
from django.urls import reverse
from django.contrib.auth.models import User
from django.utils.functional import cached_property
class Textbooks(models.Model):
owner = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True)
title = models.CharField(max_length=1000)
isbn = models.CharField(max_length=20)
author = models.CharField(max_length=250)
edition = models.CharField(max_length=50)
rrp = models.CharField(max_length=30)
about = models.TextField(max_length=1000, null=True)
textbook_image = models.FileField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse('textbooks:detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
I used Custom template tag
class CustomTag(template.Node):
def render(self, context):
context['my_custom_tag_context'] = Textbooks.objects.filter(owner=self.user.request).count()
return ''
#register.tag(name='get_custom_tag')
def get_custom_tag(parser, token):
return CustomTag()
enter image description here
AttributeError at /
'CustomTag' object has no attribute 'user'. It seems that i cant use filter in template tag.
is there any other way i can filter them and show the count by owner who is logged in?
Here is what i intend to have.
enter image description here
You have to change below line in...
user = context['request'].user
context['my_custom_tag_context'] = Textbooks.objects.filter(owner=user).count()
instead of
context['my_custom_tag_context'] = Textbooks.objects.filter(owner=self.user.request).count()
You can get user from request.
I am a beginner in Django. I am building a data model for a Django app, named PhoneReview. It will store reviews related to the latest mobile phone. It's table should include:
a. Brand – details on brand, such as, name, origin, manufacturing since, etc
b. Model – details on model, such as, model name, launch date, platform, etc
c. Review – review article on the mobile phone and date published, etc
d. Many-to-many relationship between Review and Model.
Here are my codes in models.py:
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Brand(models.Model):
brandName = models.CharField(max_length=100)
origin = models.CharField(max_length=100)
manufacturingSince = models.CharField(max_length=50, default='null')
def __str__(self):
return self.brandName
class PhoneModel(models.Model):
modelName = models.CharField(max_length=100)
launchDate = models.CharField(max_length=100)
platform = models.CharField(max_length=100)
def __str__(self):
return self.modelName
class Review(models.Model):
model_name_many_to_many = models.ManyToManyField(PhoneModel)
reviewArticle = models.CharField(max_length=1000)
datePublished = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.reviewArticle
Are my codes correct? Am I in the right direction?
Don't use camelCase in model fields. Use snake_case. Second thing is, when you want field to be default 'null', just use null=True, blank=True(optional value).
I've also provided related_name to your ManyToManyField, so you can use PhoneModelInstance.reviews.all() to get your all reviews for this specific Phone model. For large fields containing text, use TextField.
Edit
I've also added foreign key in PhoneModel which points to the Brand.
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Brand(models.Model):
brand_name = models.CharField(max_length=100)
origin = models.CharField(max_length=100)
manufacturing_since = models.TextField(null=True, blank=True)
def __str__(self):
return self.brand_name
class PhoneModel(models.Model):
brand_fk = models.ForeignKey(Brand)
model_name = models.CharField(max_length=100)
launch_date = models.CharField(max_length=100)
platform = models.CharField(max_length=100)
def __str__(self):
return self.model_name
class Review(models.Model):
phone_model = models.ManyToManyField(PhoneModel, related_name='reviews')
review_article = models.TextField()
date_published = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, null=True, blank=True)
def __str__(self):
return self.review_article
I'm trying to do a formset with the following models (boost is the primary):
class boost(models.Model):
creator = models.ForeignKey(userInfo)
game = models.ForeignKey(gameInfo)
name = models.CharField(max_length=200)
desc = models.CharField(max_length=500)
rules = models.CharField(max_length=500)
subscribe = models.IntegerField(default=0)
class userInfo(models.Model):
pic_url= models.URLField(default=0, blank=True)
auth = models.ForeignKey(User, unique=True)
birth = models.DateTimeField(default=0, blank=True)
country= models.IntegerField(default=0, blank=True)
class gameInfo(models.Model):
psn_id = models.CharField(max_length=100)
name = models.CharField(max_length=200)
publisher = models.CharField(max_length=200, default=0)
developer = models.CharField(max_length=200, default=0)
release_date = models.DateTimeField(blank=True, null=True)
I want to display a form to add a Boost item, trying to do in this way :
TrophyFormSet = inlineformset_factory(db.gameInfo, db.boost, extra=1)
formset = TrophyFormSet()
Here are my questions :
1 - When rendered, the combo box for "Creator" shows a list of "db.userInfo" (literally)! I want this to display db.userInfo.auth.username that is already in the database... how to do this?
2 - In this way, where is my "db.gameInfo" to choose?
thank you! =D
======
czarchaic answered my question very well!
But now I need just a little question:
When I use the modelform to create a form for the boost_trophy model :
class boost_trophy(models.Model):
boost = models.ForeignKey(boost)
trophy = models.ForeignKey(gameTrophyInfo)
# 0 - Obtiveis
# 1 - Requisitos minimos
type = models.IntegerField(default=0)
class gameTrophyInfo(models.Model):
game = models.ForeignKey(gameInfo)
name = models.CharField(max_length=500)
desc = models.CharField(max_length=500)
type = models.CharField(max_length=20)
It works nice, but I want the form to show in the "game" box only a really small set of items, only the: gameTrophyInfo(game__name="Game_A") results. How can I do this?
If I understand you correctly:
To change what is displayed set the model's __unicode__ function
class userInfo(models.Model):
#model fields
def __unicode__(self):
return self.auth.username