How can I implement the template using the Django model below? - django

I just started programming using Django and making a dental chart interface but having troubles making the template. I guess my challenge started with how I designed my previous models. A dental chart is composed of 32 teeth, each tooth has its own attributes. Below is my model:
class DentalChart(models.Model):
patient = models.ForeignKey(PatientInfo, on_delete=models.CASCADE)
date = models.DateField()
dentist = models.CharField(max_length=100)
PERIODONTAL_CHOICES = (
('Gingivitis', 'Gingivitis'),
('Early Periodontitis', 'Early Periodontitis'),
('Moderate Periodontitis', 'Moderate Periodontitis'),
('Advanced Periodontitis', 'Advanced Periodontitis'),
('N/A', 'N/A')
)
periodontal_screening = MultiSelectField(choices=PERIODONTAL_CHOICES, default='')
OCCLUSION_CHOICES = (
('Class(Molar)', 'Class(Molar)'),
('Overjet', 'Overjet'),
('Overbite', 'Overbite'),
('Midline Deviation', 'Midline Deviation'),
('Crossbite', 'Crossbite'),
('N/A', 'N/A')
)
occlusion = MultiSelectField(choices=OCCLUSION_CHOICES, default='')
APPLIANCES_CHOICES = (
('Orthodontic', 'Orthodontic'),
('Stayplate', 'Stayplate'),
('Others', 'Others'),
('N/A', 'N/A')
)
appliances = MultiSelectField(choices=APPLIANCES_CHOICES, default='')
TMD_CHOICES = (
('Clenching', 'Clenching'),
('Clicking', 'Clicking'),
('Trismus', 'Trismus'),
('Muscle Spasm', 'Muscle Spasm'),
('N/A', 'N/A')
)
TMD = MultiSelectField(choices=TMD_CHOICES, default='')
class Tooth(models.Model):
chart = models.ForeignKey(DentalChart, on_delete=models.CASCADE)
tooth_id = models.CharField(max_length=2)
CONDITIONS_LIST = (
('', ''),
('Present Teeth', 'P'),
('Decayed', 'D'),
('Missing due to Carries', 'M'),
('Missing (Other Causes)', 'Mo'),
('Impacted Tooth', 'Im'),
('Supernumerary Tooth', 'Sp'),
('Root Fragment', 'Rf'),
('Unerupted', 'Un')
)
condition = models.CharField(max_length=30, choices=CONDITIONS_LIST, default='')
RESTORATIONS_LIST = (
('', ''),
('Amalgam Filling', 'A'),
('Composite Filling', 'Co'),
('Jacket Crown', 'Jc'),
('Abutment', 'Ab'),
('Pontic', 'Po'),
('Inlay', 'In'),
('Implant', 'Imp'),
('Sealants', 'S'),
('Removable Denture', 'Rm')
)
restoration = models.CharField(max_length=30, choices=RESTORATIONS_LIST, default='')
I created this UI using standard HTML form so that I can visualize how it would look like. But I haven't figured out how to incorporate Django so that it would be easier to refer to the tables once I create functions such as adding and editing dental charts.
Check image here
I am having a hard time to show the dental chart on my template. I hope to get some answers and tips from the experts. Thank you!

Related

Filtering X most recent entries in each category of queryset

Question is regarding filtering X most recent entries in each category of queryset.
Goal is like this:
I have a incoming queryset based on the following model.
class UserStatusChoices(models.TextChoices):
CREATOR = 'CREATOR'
SLAVE = 'SLAVE'
MASTER = 'MASTER'
FRIEND = 'FRIEND'
ADMIN = 'ADMIN'
LEGACY = 'LEGACY'
class OperationTypeChoices(models.TextChoices):
CREATE = 'CREATE'
UPDATE = 'UPDATE'
DELETE = 'DELETE'
class EntriesChangeLog(models.Model):
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
)
object_id = models.PositiveIntegerField(
)
content_object = GenericForeignKey(
'content_type',
'object_id',
)
user = models.ForeignKey(
get_user_model(),
verbose_name='user',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='access_logs',
)
access_time = models.DateTimeField(
verbose_name='access_time',
auto_now_add=True,
)
as_who = models.CharField(
verbose_name='Status of the accessed user.',
choices=UserStatusChoices.choices,
max_length=7,
)
operation_type = models.CharField(
verbose_name='Type of the access operation.',
choices=OperationTypeChoices.choices,
max_length=6,
)
And I need to filter this incoming queryset in a such way to keep only 4 most recent objects (defined by access_time field) in each category. Categories are defined by ‘content_type_id’ field and there are 3 possible options.
Lets call it ‘option1’, ‘option2’ and ‘option3’
This incoming queryset might contain different amount of objects of 1,2 or all 3 categories. This is can’t be predicted beforehand.
DISTINCT is not possible to use as after filtering operation this queryset might be ordered.
I managed to get 1 most recent object in a following way:
# get one most recent operation in each category
last_operation_time = Subquery(
EntriesChangeLog.objects.filter(user=OuterRef('user')).values('content_type_id').
annotate(last_access_time=Max(‘access_time’)).values_list('last_access_time', flat=True)
)
queryset.filter(access_time__in=last_operation_time)
But I have a hard time to figure out how to get last 4 most recent objects instead of last one.
This is needed for Django-Filter and need to be done in one query.
DB-Postgres 12
Do you have any ideas how to do such filtration?
Thanks...
pk_to_rank = queryset.annotate(rank=Window(
expression=DenseRank(),
partition_by=('content_type_id',),
order_by=F('access_time').desc(),
)).values_list('pk', 'rank', named=True)
pks_list = sorted(log.pk for log in pk_to_rank if log.rank <= value)
return queryset.filter(pk__in=pks_list)
Managed to do it only this way by spliting queryset in 2 parts. Option with 3 unions is also possible but what if we have 800 options instead 3 - make 800 unions()??? ges not...

How to count with filter django query?

I'm trying to get a list of latest 100 posts and also the aggregated count of approved, pending, and rejected posts for the user of that post.
models.py
class BlogPost(models.Model):
POST_STATUSES = (
('A', 'Approved'),
('P', 'Pending'),
('R', 'Rejected')
)
author = models.ForeignKey(User)
title = models.CharField(max_length=50)
description = models.TextField()
status = models.ChoiceField(max_length=1, choices=POST_STATUSES)
views.py
def latest_posts(request)
latest_100_posts = BlogPost.objects.all()[:100]
I get the latest 100 posts, now I want to get each author of the post and display their total Approved, Pending, Rejected count
Title Of Post, Author1, 10, 5, 1
Title Of Post2, Author2, 7, 3, 1
Title Of Post3, Author1, 10, 5, 1
...
Some things I've thought about are looping through each of the 100 posts and returning the count, but it seems very inefficient
for post in latest_100_posts:
approved_count = BlogPost.objects.filter(author=post.user,status='A').count()
pending_count = BlogPost.objects.filter(author=post.user,status='P').count()
rejected_count = BlogPost.objects.filter(author=post.user,status='R').count()
Is there a more efficient way to do this? I know about using aggregate Count but I'm not sure how to sub-filter on the status ChoiceField
You can do it like this using conditional aggregation:
For that, lets add related_name in BlogPost.
class BlogPost(models.Model):
POST_STATUSES = (
('A', 'Approved'),
('P', 'Pending'),
('R', 'Rejected')
)
author = models.ForeignKey(User, related_name="user_posts")
title = models.CharField(max_length=50)
description = models.TextField()
status = models.ChoiceField(max_length=1, choices=POST_STATUSES)
Then lets update the queryset:
from django.db.models import Count, Case, When, IntegerField
top_post_users = list(BlogPost.objects.values_list('auther_id', flat=True))[:100]
users = User.objects.filter(pk__in=top_post_users).annotate(approved_count=Count(Case(When(user_posts__status="A", then=1),output_field=IntegerField()))).annotate(pending_count=Count(Case(When(user_posts__status="P", then=1),output_field=IntegerField()))).annotate(reject_count=Count(Case(When(user_posts__status="R", then=1),output_field=IntegerField())))
users.values('approved_count', 'pending_count', 'reject_count')
from django.db.models import Count
approved_count = BlogPost.objects.filter(author=post.user, status=‘A’).annotate(approved_count=Count(‘id’))
pending_count = BlogPost.objects.filter(author=post.user, status=‘P’).annotate(approved_count=Count(‘id’))
rejected_count = BlogPost.objects.filter(author=post.user, status=‘R’).annotate(approved_count=Count(‘id’))

how to get the latest in django model

In this model:
class Rank(models.Model):
User = models.ForeignKey(User)
Rank = models.ForeignKey(RankStructure)
date_promoted = models.DateField()
def __str__(self):
return self.Rank.Name.order_by('promotion__date_promoted').latest()
I'm getting the error:
Exception Value:
'str' object has no attribute 'order_by'
I want the latest Rank as default. How do I set this?
Thanks.
Update #1
Added Rank Structure
class RankStructure(models.Model):
RankID = models.CharField(max_length=4)
SName = models.CharField(max_length=5)
Name = models.CharField(max_length=125)
LongName = models.CharField(max_length=512)
GENRE_CHOICES = (
('TOS', 'The Original Series'),
('TMP', 'The Motion Picture'),
('TNG', 'The Next Generation'),
('DS9', 'Deep Space Nine'),
('VOY', 'VOYAGER'),
('FUT', 'FUTURE'),
('KTM', 'KELVIN TIMELINE')
)
Genre = models.CharField(max_length=3, choices=GENRE_CHOICES)
SPECIALTY_OPTIONS = (
('CMD', 'Command'),
('OPS', 'Operations'),
('SCI', 'Science'),
('MED', 'Medical'),
('ENG', 'Engineering'),
('MAR', 'Marine'),
('FLT', 'Flight Officer'),
)
Specialty = models.CharField(max_length=25, choices=SPECIALTY_OPTIONS)
image = models.FileField(upload_to=image_upload_handler, blank=True)
This is the Rank_structure referenced by Rank in Class Rank.
THe User Foreign key goes to the standard User table.
The reason that you’re getting an error is because self.Rank.Name is not a ModelManager on which you can call order_by. You’ll need an objects in there somewhere if you want to call order_by. We can’t help you with the django formatting for the query you want unless you also post the model definitions as requested by several commenters. That said, I suspect that what you want is something like:
def __str__(self):
return self.objects.filter(Rank_id=self.Rank_id).order_by('date_promoted').latest().User.Name

How to get number of items grouped by a property of an intermediate model

I would like to have something like this
Adventure (4) | Sci-fi (12)
which are the books, in a bookshop, linked by a local price.
Say, Hobbit is $5 at Amazon and $6 at Barnes. So if I was listing the books in Amazon I will have Adventure (1) as the count of the books with a specified price in amazon.
If I do like this I get the correct Genres:
for u in Bookshop.objects.get(pk=1).BookBookshopLink_set.all():
print u.book.genre
which would print, e.g.:
Sci-fi
Sci-fi
Adventure
Here are the models:
from parler.models import TranslatableModel, TranslatedFields
from parler.managers import TranslationManager
class Genre(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200),
slug=models.SlugField(),
description=models.TextField(blank=True),
meta={'unique_together': [('language_code', 'slug')]},
)
published = models.BooleanField(default=False)
class Book(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200),
slug=models.SlugField(),
description=models.TextField(blank=True),
meta={'unique_together': [('language_code', 'slug')]},
)
genre = models.ForeignKey(Genre, blank=True, null=True)
published = models.BooleanField(default=False)
class Bookshop(TranslatableModel):
translations = TranslatedFields(
name=models.CharField(max_length=200),
description=models.TextField(_('Description'), default='', blank=True),
slug=models.SlugField(),
meta={'unique_together': [('slug', 'language_code')]},
)
booklist = models.ManyToManyField(Book, blank=True, through='BookBookshopLink')
class BookBookshopLink(TranslatableModel):
bookshop = models.ForeignKey(Bookshop)
book = models.ForeignKey(Book)
price = models.IntegerField(blank=True, null=True)
To do what you're trying to achieve in one query, you need to use Count, annotate and values_list
I'll show you a code example and then I'll try to explain it:
from django.db.models import Count
from your_project.models import *
Genre.objects.all().values_list('name').annotate(num_books=Count('book'))
.values_list('name'): This return a list of all genres by name
.annotate(num_books=Count('book')): This count books for each Genre
I have a similar models structure in my projects and when I execute that code, I get this as answer:
[(u'GENRE_NAME', 13), (u'GENRE_NAME', 14), (u'GENRE_NAME', 0),...]
You can parse the output of this query to fit your expectations
I also recomend you to check oficial documentation Django Agreggation
This loops over all your genres and prints out how many of them there are.
Even if there are 0 in a genre.
for a_genre in Gendre.objects.all():
print Book.objects.filter(genre=a_genre).count()
and if you want it printed with the genre aswel
for a_genre in Gendre.objects.all():
print "%s (%d)" % (a_genre, Book.objects.filter(genre=a_genre).count())
Documentation for filters in django : https://docs.djangoproject.com/en/1.7/topics/db/queries/#retrieving-specific-objects-with-filters

ManyToMany relations and Intermediate models (newbie guidance)

I'd really appreciate some guidance on how to construct the following models. I want to make sure I'm doing this in the ideal way.
I want to keep track of some info about employees at my work. Basically, past Education and past Work Experience
So here's the relations I can think of
Education
An employee could been to several school and each school can have many students (m2m)
Students attend a school for a period of time
a student can have multiple degrees form the same school and that school offers multiple degrees (m2m)
Work (almost the same as education)
m2m relation with employees and companies
employees work at a company to a set time
employes could have several jobs at one company (m2m)
Following basically what's above, here's the code I've worked out:
#make a list of numbers in a tuple
START_DATE_CHOICES = dict((str(x),str(x)) for x in range(date.today().year-30,date.today().year+1))
END_DATE_CHOICES = START_DATE_CHOICES
START_DATE_CHOICES = START_DATE_CHOICES.items()
START_DATE_CHOICES.sort()
START_DATE_CHOICES.reverse()
START_DATE_CHOICES = tuple(START_DATE_CHOICES)
END_DATE_CHOICES['current'] = 'current'
END_DATE_CHOICES = END_DATE_CHOICES.items()
END_DATE_CHOICES.sort()
END_DATE_CHOICES.reverse()
END_DATE_CHOICES = tuple(END_DATE_CHOICES)
#both schools and companies
class Institution(models.Model):
name = models.CharField(max_length = 75)
class Education(models.Model):
DEGREE_CHOICES = (
('A.A.', 'Associate'),
('Minor', 'Minor'),
('B.A.', 'Bachelor of Arts'),
('B.S.', 'Bachelor of Science'),
('Masters', 'Masters'),
('Ph. D.', 'Doctor of Philosophy'),
)
school = models.ManyToManyField(Institution, through='Dates')
degree = models.CharField(max_length = 5, choices = DEGREE_CHOICES, null = True)
subject = models.CharField(max_length = 20, null = True)
class Work(models.Model):
company = models.ManyToManyField(Institution, through='Dates')
position = models.CharField(max_length = 50, null = True)
jobDescription = models.TextField(null = True)
class Dates(models.Model):
education = models.ForeignKey(Education, null = True)
work = models.ForeignKey(Work, null = True)
institution = models.ForeignKey(Institution)
start = models.CharField(max_length = 4, choices = START_DATE_CHOICES)
end = models.CharField(max_length = 7, choices = END_DATE_CHOICES)
class Person(models.Model):
....
school = models.ManyToManyField(Institution, blank=True)
education = models.ManyToManyField(Education, blank = True)
company = models.ManyToManyField(Institution, blank = True, related_name="%(app_label)s_%(class)s_related")
work = models.ManyToManyField(Work, blank=True)
....
So my question is: Is this an acceptable way of doing it? I'm a newbie and I'm not sure if I'm just abusing relationships
also, I'm sure there's a easier way to do the whole start/end date thing... wasn't able to figure it out though
Any pointers would be greatly appreciated, Thank you!
For many to many fields you should use the plural form. for Person that would be educations and jobs for instance.
Also it is convention to write job_description instead of jobDescription in model fields.
In Person the fields school and company are redundant because that information is accessible through educations (currently education) and jobs (currently work) respectively.
I think you don't need the many to many field company in Work. A simple ForeignKey should suffice. I think you only have a specific job at ONE company for a given time. You can have mutliple jobs at the same time at multiple companies but still every job is at a single employer. Or am I wrong?
The same goes for Education. You only have a degree in a specific subject from one school even if you went to several schools in the course of this education. Or do you want to model that situation that accurately?
The naming of all dates is a bit misleading since they all are acually years. You also could use NULL in the end year as 'current' and use PositiveIntegerFields.
Now I would have this code:
#both schools and companies
class Institution(models.Model):
name = models.CharField(max_length = 75)
class Education(models.Model):
DEGREE_CHOICES = (
('A.A.', 'Associate'),
('Minor', 'Minor'),
('B.A.', 'Bachelor of Arts'),
('B.S.', 'Bachelor of Science'),
('Masters', 'Masters'),
('Ph. D.', 'Doctor of Philosophy'),
)
school = models.ForeignKey(Institution)
degree = models.CharField(max_length = 5, choices = DEGREE_CHOICES, null = True)
subject = models.CharField(max_length = 20, null = True)
start_year = models.PositiveIntegerField()
end_year = models.PositiveIntegerField(null=True)
class Job(models.Model):
company = models.ForeignKey(Institution)
position = models.CharField(max_length = 50, null = True)
job_description = models.TextField(null = True)
start_year = models.PositiveIntegerField()
end_year = models.PositiveIntegerField(null=True)
class Person(models.Model):
....
educations = models.ManyToManyField(Education, blank = True)
jobs = models.ManyToManyField(Work, blank=True)
....
Of course if you want to have choices for the years you can have them
YEAR_CHOICES = ((year, str(year)) for year in range(...))
Alternatively you could write a custom validator for your year fields.