I am creating a small application to help our Training Department manage their cirriculum using Django. When we talk about students we have two type; Employee and Customer.
Since all of the employees will be in auth_user I would rather not have to populate another table with that data. The behavior that I want is when a Class is displayed in the Django Admin I would like one control to be filled with data from two tables for the student list. Is this even possible in Django. My suspicion is that it is not. This is what I am messing around with:
from django.db import models
from django.contrib.auth.models import User
class Student(models.Model):
cell_phone = models.CharField(max_length=14)
class Meta:
abstract = True
class EmployeeStudent(models.Model, Student):
user = models.OneToOneField(User)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class CustomerStudent(models.Model, Student):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.CharField(max_length=25)
However, just thinking about it does it make more sense to do:
from django.db import models
from django.contrib.auth.models import User
class Student(models.Model):
user = models.ForeignKey(User, null=True, blank=True, default=None)
first_name = models.CharField(max_length=25, null=True, blank=True, default=None)
last_name = models.CharField(max_length=25, null=True, blank=True, default=None)
email = models.CharField(max_length=25, null=True, blank=True, default=None)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
I cringe at the thought of a blank record.
Any recommendations? Does it make sense to add everything to auth_user and leave the staff flag to false then just use a one to one field to map an auth_user to a Student? I do not really want to do that if I don't have to because I am not going to give anyone else access to the auth_user table so all additions to this table would need to be done by me.
You could try to use model inheritance (i.e User model inheritance : https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-user) for your Student model, or mixing Student(models.Model) with User in your EmployeeStudent and CustomerStudent models :
class Student(models.Model):
cell_phone = models.CharField(max_length=14)
class Meta:
abstract = True
class EmployeeStudent(User, Student):
user = models.OneToOneField(User)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class CustomerStudent(User, Student):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.CharField(max_length=25)
or :
class Student(User):
cell_phone = models.CharField(max_length=14)
class Meta:
abstract = True
class EmployeeStudent(models.Model):
user = models.OneToOneField(Student)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class CustomerStudent(models.Model):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.CharField(max_length=25)
If I understand correctly, you'd then want to display Customers as well as Employees in the same changelist in admin ? Using Student(User) with Employee / Customer as inlines might solve your problem.
Hope this helps,
Regards
This is a fairly common use-case (i.e. different types of userprofiles) in Django. In your case, I think the approach below would suit your scenario:
from django.db import models
from django.contrib.auth.models import User
class Student(models.Model):
STUDENT_TYPES = (
('E', 'EmployeeStudent'),
('C', 'CustomerStudent'),
)
user = models.OneToOneField(User, null=True, blank=True, default=None)
user_type = models.CharField(max_length=1, choices=STUDENT_TYPES)
class EmployeeDetails(models.Model):
student = models.OneToOneField(Student)
extension = models.CharField(max_length=4, null=True, blank=True, default=None)
class StudentDetails(models.Model):
student = models.OneToOneField(Student)
# BTW: all the fields below are redundant since they are already in User
first_name = models.CharField(max_length=25, null=True, blank=True, default=None)
last_name = models.CharField(max_length=25, null=True, blank=True, default=None)
email = models.CharField(max_length=25, null=True, blank=True, default=None)
This way, you can check the student.user_type and infer if you need to get EmployeeDetails or StudentDetails
NOTE:: Even though this is a recommended approach, it is not quite easy to enter data using the default admin interface in this manner. You might want to see how profile inlines are done to show the user profile fields in the admin as well.
Related
I am a newbie and I am designing Django authentication software
My problem is that I have to check four separate levels for which there are predefined codes in the permission check.
A model class named province and a model class named city and a model named Student
My review model is such that I should be able to give a user the membership of the central office or the membership of a province or the membership of a city in a province.
The user can only be for one of these situations
My models is like this
class Province(models.Model):
province_id = models.IntegerField(primary_key=True, serialize=True, verbose_name='ID')
province_title = models.CharField(max_length=30, verbose_name=_('Province'))
class Meta:
ordering = ['province_id']
def __str__(self):
return self.province_title
class Center(models.Model):
id = models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
center_title = models.CharField(max_length=150, verbose_name=_('Name of the training center'), null=True)
center_title_id = models.CharField(max_length=64, verbose_name=_('Code of the training center'), null=True)
province = models.ForeignKey(Province, on_delete=models.CASCADE, verbose_name=_('Province'))
class Meta:
verbose_name = _("center")
verbose_name_plural = _("centers")
def __str__(self):
return self.center_title`
class CulturalUser(AbstractUser):
pass
class UserPositions(models.Model):
class Position(models.TextChoices):
UNIVERSITY_STUDENT = '1', _('University student')
TRAINING_CENTER = '2', _('Training center')
PROVINCE = '3', _('Province')
CENTRAL_OFFICE = '4', _('Central office')
user = models.ForeignKey(CulturalUser, on_delete=models.CASCADE, verbose_name=_('User'))
position = models.CharField(max_length=2, verbose_name=_('Access level'), choices=Position.choices,
default=Position.UNIVERSITY_STUDENT)
province = models.ForeignKey(Province, on_delete=models.SET_NULL, blank=True, null=True)
center = models.ForeignKey(Center, on_delete=models.SET_NULL, blank=True, null=True)
def __str__(self):
return self.position
please direct me
According to the search I did, I could not find the right sources in my question mode
You can either extend or substitute an user model. Quotting the documentation:
If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you. This model behaves identically to the default user model, but you’ll be able to customize it in the future if the need arises:
A full example
Django's user models counts with permissions and authorization and groups out of the box. So, instead of choices fields you would have four groups, for which you would handle access with permissions.
University student
Training center
Province
Central office
Here is one version of your model:
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.contrib.auth.models import AbstractUser
class Province(models.Model):
name = models.CharField(
max_length=30,
verbose_name=_('Province')
)
class Meta:
ordering = ['id']
def __str__(self):
return self.title
class Center(models.Model):
name = models.CharField(
max_length=150,
verbose_name=_('Name of the training center'),
null=True,
)
code = models.CharField(
max_length=64,
verbose_name=_('Code of the training center'),
null=True,
)
province = models.ForeignKey(
Province,
on_delete=models.CASCADE,
verbose_name=_('Province')
)
class Meta:
verbose_name = _("center")
verbose_name_plural = _("centers")
def __str__(self):
return self.name
class User(AbstractUser):
province = models.ForeignKey(
Province,
on_delete=models.SET_NULL,
blank=True, null=True,
)
center = models.ForeignKey(
Center,
on_delete=models.SET_NULL,
blank=True, null=True,
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
Note that I used AbstractUser, like you did, but you can do an even deeper customization with AbstractBaseUser, as shown in the example I linked above.
I have two models, User and Card. One user has one card. I need to get objects of User if the 'card_written' of Card model is False in a view.
class User(model.Model):
phone = PhoneNumberField(null=False, blank=False, unique=True)
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class Card(models.Model):
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
card_number = models.CharField(max_length=200)
card_written = models.BooleanField(default=False, null=False)
card_write_date = models.DateTimeField(null=True, blank=True)
card_delivered = models.BooleanField(default=False, null=False)
You can query on related models quite easily in Django by separating related fields by double underscores.
A query like this should work for you:
User.objects.filter(card__card_written=False)
Here card is the related name since you haven't specified it in the Card model if you want it to be something else you need to specify like so in the Card model:
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE, related_name='something_else')
I have these models:
class Country(models.Model):
name = models.CharField(unique=True, max_length=50)
class Person(models.Model):
name = models.CharField(max_length=50, blank=True, null=True)
country = models.ForeignKey(Country, models.DO_NOTHING, db_column='countries', blank=True, null=True)
class Organization(models.Model):
name = models.CharField(max_length=50, blank=True, null=True)
class Membership(models.Model):
person= models.ForeignKey(Person, models.DO_NOTHING, db_column='people', blank=True, null=True)
organization= models.ForeignKey(Organization, models.DO_NOTHING, db_column='organizations', blank=True, null=True)
There are a high number of people (over 1000) that can be members of several organizations.
I have a view in the admin page as follows
class MembershipInline(admin.StackedInline ):
model = Membership
extra = 1
class OrganizationAdmin(admin.ModelAdmin):
inlines = [MembershipInline]
admin.site.register(Organization,OrganizationAdmin)
Therefore I can manage any organization and see the people that belongs to it. I can also add new people to the organization.
The problem is that the number of people is too high and the list shows too many of them. Is there any way to filter this StackedInline?
For example placing another field in the Inline where a country can be chosen to filter the people that can be added, or a search field to filter the person's name.
Thanks in advance!
Finally got to work using the option "autocomplete_fields "
class PersonAdmin(admin.ModelAdmin):
search_fields = ['country__name','name']
admin.site.register(Person,PersonAdmin)
class MembershipInline(admin.StackedInline ):
model = Membership
extra = 1
autocomplete_fields = ['person',]
How it looks
I have the two models, One is the User model and the other is a Contact model.
The Contact model is as follows:
class Contact(models.Model):
pass
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, null=True, on_delete=models.CASCADE, verbose_name=_('User'), related_name="user")
contact = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, null=True, on_delete=models.CASCADE, verbose_name=_('Contact'), related_name="contact")
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=False)
is_contact = models.BooleanField(default=False)
Basically, what is does is create a contact for a user. Kind of a friend request and accept simple model. So the loggedIn user(request.user) could be either the contact.user or contact.contact.
And the settings.AUTH_USER_MODEL is a CustomUser model:
class CustomUser(AbstractUser):
pass
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=20, default="", blank=True)
last_name = models.CharField(max_length=20, default="", blank=True)
How can I create a relationship, where I can get a users contact by doing something like this:
// get user contacts user=CustomUser.objects.get(pk=1)
user.contacts.objects.all()
Typically you query reverse relations using the related_name keyword, which is contact_set by default:
user.contact_set.all()
If you want to change the name of this reverse relationship, you need to do so in the Contact model using related_name:
class Contact(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="contacts", ...)
user.contacts.all()
I think that my problem here is Django specific and not necessarily a reflection on
my understanding of relational databases (hopefully).
I have a Django app that stores information on Contacts.
With that one table things seemed to work fine. When I wanted to categorize
the type of relationship - is this a professional relationship, family, friends, etc.
That's when things didn't show up like I wanted. I finally got the migration to work
with the new table.
I'm using python 3 with the latest version of django. I have a mysql
database. I want a one to many relationship, where one contact can
be characterized by many categories. When I work with the django admin
and try to enter a contact, I'm not seeing a field for entering relationship categories.
So, here is my models.py for the contacts app.
from django.db import models
class Resource(models.Model):
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
organization = models.CharField(max_length=60, null=True, blank=True)
street_line1 = models.CharField("Street Line 1", max_length=50, null=True, blank=True)
street_line2 = models.CharField("Street Line 2", max_length=50, null=True, blank=True)
city = models.CharField(max_length=40, null=True, blank=True)
state = models.CharField(max_length=40, null=True, blank=True)
zipcode = models.CharField(max_length=20, blank=True, null=True)
phone1 = models.CharField(max_length=20, null=True, blank=True)
phone2 = models.CharField(max_length=20, null=True, blank=True)
email = models.EmailField(max_length=60, null=True, blank=True)
website = models.URLField(max_length=90, null=True, blank=True)
def __str__(self):
return "%s %s \t%s" % (self.first_name, self.last_name, self.organization)
class Meta:
ordering = ('last_name',)
class Relationship(models.Model):
category = models.CharField(max_length=120)
resource = models.ForeignKey(Resource, related_name='category')
def __str__(self):
return self.category
class Meta:
ordering = ('category',)
Thanks in advance for any help,
Bruce