I am creating a Django app and this questions comes to hit me again (I've already run into it but I resolved it in a way I don't think is correct).
So I have these models:
def Person(models.Model):
name = models.CharField(max_length=30)
status = models.ManyToManyField(Status)
def Status(models.Model):
name = models.CharField(max_length=30)
Any person may have up to 3 status (or none). And this was supposed to be a string array containing only a few possible status. e.g. happy, lucky, strong.
However a CharField with choices would not be possible here, as one person may feel happy and strong at the same time.
Is my current approach the best one possible?
You will need to validate the statuses count for each person in the clean method.
from django.db import models
from django.core.exceptions import ValidationError
class Status(models.Model):
name = models.CharField(max_length=30)
class Person(models.Model):
name = models.CharField(max_length=30)
status = models.ManyToManyField(Status)
def clean(self):
# check person status count
if self.status.all().count() > 3:
raise ValidationError("Person can not have more than 3 statuses")
Update
Since it's a ManyToMany relationship, you will never get this validated during the creation of the object, unless you have a separate form for adding statuses for persons.
If you have statuses field in the same form of person creation, then this check MUST be in the form.
class PersonForm(forms.ModelForm):
class Meta:
model = Person
def clean(self):
statuses = self.cleaned_data.get('status')
if status.count() > 3:
raise ValidationError("Person can not have more than 3 statuses")
Why this method
This design of models will allow you to have many different ways of querying. such as get people who are happy! or count of people who are sad and finally get people with similar statuses
Just a quick addition - if you're using PostgreSQL (and that's probably the best decision) you could also use an Array Field, for which you can specify a max size.
https://docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields/
Another option is to use a tagging library like Django-taggit
Related
Using Django we have two types of Users (Teachers and Students) with common fields and uncommon fields
In our wizard we first POST the common fields to /users with an extra type_field
Every operation after this should be able to figure out which model (Teacher or Student) it needs to use.
We are thinking of making two models ( Teacher and Student ) with an one-to-one field.
But how do we hookup the type_field to the right Model on every operation?
You dont have to go for an extra field since you are already having two different classes for students and teachers. A simple approach may looks like below.
from django.contrib.auth.models import User
class Teacher(User):
extra_field_1 = models.Fieldtype()
...
...
class Student(User):
extra_field_1 = models.Fieldtype()
...
...
You can provide both type of users same registration form and upon clicking next take them to next page based on the value of I am a teacher/student field. In that case I suggest you to use atomic blocks if you dont want to save data in case registration procedure fails at some point or user have selected a wrong choice and they want to go back. By this approach each inherited models have username, first_name, last_name and email that you dont have to insert any of these to Teacher or student model.
Then you have to create forms for each model. You may use modelform A much better approach will be using class based views since that reduce a lot of code and stick to dry principles.
You may use something like:
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
# more common fields
class Student(Person):
specific_field_to_student = models.CharField(max_length=255)
# more fields
class Teacher(Person):
specific_field_to_teacher = models.CharField(max_length=255)
# more fields
In your database you will have 3 tables (yourapp_person, yourapp_student and yourapp_teacher). Now, if type_field value is student, you will use Student model to create user, if it is teacher, you will use Teacher model.
Note: You may need to make Person model above a subclass of the built-in User model.
Edit:
I have edited the model above to take into account the requirements in the comments below.
Now, to retrieve user by id, you can use the following code in your view:
user = Person.objects.get(id=id) # id is the view param
if hasattr(user, 'student'):
print("Student")
else: # hasattr(user, 'teacher')
print("Teacher")
In my project I have to deal with different type of users e.g. costumers and employers. Each type of user have its own fields and permissions: costumer can buy things whereas employer can not.
I have read the Django docs and it looks like there are two options:
Costumize the AbstractUser class and adding all the fields of costumers and employers. Then use the permission system to grant/revoke permission or create a group for each type of user. The downside here is that there unused fields.
Adopt the proxy model:
from django.contrib.auth.models import User
from django.db import models
class Costumer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
db_table = 'costumers'
permissions = (
("access_this", "User may access this"),
)
ordering = []
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
db_table = 'employers'
permissions = (
("access_that", "User may access that"),
)
ordering = []
This case seems more reasonable but I don't know how to deal with the permissions. Consider I'd like to use #permission_required instead of checking the type (if the user has a specific field) because it seems more legit for Django system.
So in the end, what is the best way to approach such scenario?
The first solution is much better.
The downside here is that there unused fields.
I disagree with this, you don't have to store all the fields within the User model. Also, if you're talking about 5 fields for example, that doesn't really matter.
You can extend AbtractUser and also use some composition; you don't have to put all the fields there:
class User(AbstractUser):
email = ...
...
# for the employer, let's say you want to save the company details
company = models.ForeignKey('myapp.Company', null=True, blank=True)
...
# for the customer
customer_details = models.ForeignKey('...')
This way you could record a user_type if you want or deduce the type from the foreign key (if there is a company, it's an employer).
To help you more with the model, I need to know what differentiate an employer from a customer in your application. Note that with that solution, an instance of User could be both.
Concerning the permissions, I feel like it's a separate problem; I'd recommend you to sort it last. If the design you pick is close to the reality and you get the features working; adding custom permissions will be really easy.
Assume some Company with Employees. There are Name and Contact information bound to each Employee. Each Contact contains Street and Phones fields.
What I want is a page which lists employees within a company. But everything must be listed as forms. Because I want to be able to modify the particular Employee information and the most important - I want to be able to add new Employees (clicking a button "Add new employee" must add a new empty "Employee form"). As well as it must allow to add a new phone number to the existing Employee's Contact information any time.
The data model looks like:
--Company
----Employee1
------Name
------Contact
--------Street
--------Phones
----------Phone1
----------Phone2
----Employee2
------Name
------Contact
--------Street
--------Phones
----------Phone1
----------Phone2
----------Phone3
...
Could someone please help to design Models and Forms for such a task? Your help is very much appreciated. Many thanks!
P.S. Forgot to mention that I want all the data "collected" in the Company object at the end of the day. I mean when I serialize c = Comapany.objects.all()[0] on the back end the entire employee information must be visible, like c.employees[0].contact.phones[0] must be the first employee's first phone number. Thanks.
P.P.S.
That is not the case that I'm just forwarding my project. This is just an hypothetical example I'd created to present the problem. I'm a django newbie and trying to understand how the framework gets things rolling.
I've spent lot of time on this. I've found several ways to go, but no one got me to the end. For instance, a wonderful blog about nested formsets http://yergler.net/blog/2013/09/03/nested-formsets-redux/ helped with forms and rendering. But, it solved only the half of the problem. The data like I mentioned above is not "being collected" into an object. At the end of the day I want to serialize a Company object and save it in yaml format using pyyaml (see my previous post django: want to have a form for dynamically changed sequence data).
Django is perfect with "static" models and forms, ModelForms are awesome. But what if your model needs to be changed dynamically? No standard way to go. Either no appropriate documentation nor I could find a one. Thus, I'd like to hear how experts imagine the solution for such a problem.
Try this:
from django.db import models
class _Contact(object):
pass
class Company(models.Model):
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
#property
def employees(self):
return self.employee_set.prefetch_related('phones').order_by('-created_at')
class Phone(models.Model):
number = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
class Employee(models.Model):
name = models.CharField(max_length=255)
street = models.CharField(max_length=255)
phones = models.ManyToManyField('Phone', through='EmployeePhone', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
company = models.ForeignKey(Company)
#property
def contact(self):
_contact = _Contact()
_contact.street = self.street
_contact.phones = self.phones.order_by('-employeephone__created_at')
return _contact
class EmployeePhone(models.Model):
employee = models.ForeignKey(Employee)
phone = models.ForeignKey(Phone)
created_at = models.DateTimeField(auto_now_add=True)
However, you should just use employee.street and employee.phones. employee.contact is redundant.
For example, I have 2 models:
class User(AbstractUser):
achievement = models.ManyToManyField('Achievement')
class Achievement(models.Model):
name = models.CharField(max_length=50)
I need get all achievements of several users, i think, it should be something like this:
for user in User.objects.filter(bla-bla):
achievement_list += user.achievement
But it returns error "'ManyRelatedManager' object is not iterable"
user.achievement is a ManyRelatedManager, similar to how, say, Achievement.objects is a Manager. Try instead user.achievement.all() or user.achievement.filter(...) (with whatever filter you may want).
I'm trying to wrap my head around how I would structure my database tables in the Django webapp I'm writing. I'm a relative newbie to web development, but this is the very first time I've tried to use a database, so bear with me if it's a stupid question.
The webapp goes through each Oscar the Academy gives out and allows the user to select which of some (varying) number of nominations will win an Oscar. The data from each individual session will be publicly available by going to a url like [url].com/answers/[unique id]. The overall data will also be available on a results page. So I've started writing my models file, and this is what I have so far:
from django.db import models
class Nominee(models.Model):
award = models.CharField(max_length=50)
title = models.CharField(max_length=50)
key = modelsCharField(max_length=50)
subtitle = models.CharField(max_length=50)
numVotes = models.IntegerField()
class Session(models.Model):
id = models.IntegerField() # unique id of visitor
bpictureVote = models.ForeignKey(Nominee, related_name = 'nom')
bactorVote = models.ForeignKey(Nominee, related_name = 'nom')
# ... for each award
I was originally thinking of having
class Award(models.Model):
name = models.CharField(max_length=50)
and at the beginning of Nominee,
award = models.ForeignKey(Award, related_name = 'award')
but I couldn't figure out why that would be better than just having award be a part of the Nominee class.
This is really just a start, because I've gotten a bit stuck. Am I on the right track? Should I be doing this totally differently (as I probably should...)? Any thoughts?
Thanks!
You are on the right track.
You need a separate Award class to avoid having to type in award's name every time you create a Nominee. By having a ForeignKey reference you make sure that you can safely rename your award, add additional information about the award (let's say in the future you decide to give each award a separate page with a description and a list of nominees), you also avoid errors which can happen from having a set of different spellings and typos ("Best Engineer Award" and "Best Engineer award"). It also makes sense - your application operates a set of objects: user sessions, nominees and awards.
Few unrelated notes:
You don't need an explicit Session.id field, django ORM creates it for you.
Property names have to be name_with_underscores, not camelCase.
No spaces around "=" in an arguments list: models.ForeignKey(Nominee, related_name='nom').
4 spaces instead of 2 (unless explicitly otherwise specified).
I am not entirely sure, because you do mention multiple nominees per award (assuming this is something like a poll before the actual nomination) a ManyToMany would be your required relation, in order to use also the additional user data.
But in the case you have implemented this as a specific app for nominations and implemented a custom user model then this would be refactored to something else...
Anyway to your current implementation:
class Nominee(models.Model):
title = models.CharField(max_length=50)
key = modelsCharField(max_length=50)
subtitle = models.CharField(max_length=50)
...
class Award(models.Model):
name = models.CharField(max_length=50)
nominees = models.ManyToManyField(Nominee, through='AwardNominees')
...
class AwardNominees(models.Model):
nominee = models.ForeignKey(Nominee)
award = models.ForeignKey(Award)
user = models.ForeignKey(User)
numVotes = models.IntegerField()
....
So it turned out I was thinking about this entirely wrong. I've now completely changed things, and now it's fully functional (!). But in the spirit of full disclosure, I should say that it definitely may not be the best solution. It sure seems like a good one, though, because it's really simple. Now I have only one model:
class Vote(models.Model):
award = models.CharField(...) # Name of the award
title = models.CharField(...) # Title of the nominee
subtitle = models.CharField(...) # Subtitle of the nominee
uid = models.CharField(...) # A 6 character user ID for future access
When I want to show the results of one user's votes, I can use Django's database tools to filter for a certain uid captured in the URL. When I want to tally the votes, I can use a combination of filters and Django's count() to determine how many votes each nominee had for a certain award. Sounds reasonable enough to me!