I am having some trouble in selecting data in Django.
models.py
class Location(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
my_location = models.CharField(max_length=120, choices=LOCATION_CHOICES)
update_date = models.DateField(auto_now=True, null=True)
date = models.DateField()
def __str__(self):
return self.my_location
class UserProfile(models.Model):
user = models.ForeignKey(User)
user_base = models.CharField(max_length=120, choices=LOCATION_CHOICES)
user_position = models.CharField(max_length=120)
user_phone = models.PositiveIntegerField()
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.user)
super(UserProfile, self).save(*args, **kwargs)
def __unicode__(self):
return self.user.username
views.py
def index(request):
locations = Location.objects.order_by('-update_date')
context = {'locations': locations}
return render(request, 'index.html', context)
I was able to show the email from User module but what I really want to show is the data from UserProfile.
Please, any advice.
Thank you.
Instead of using
user = models.ForeignKey(User)
use:
user = models.OneToOneField(User)
One-to-one relationships suit better your case. If you use them, your User model will automatically get a userprofile attribute that you can use like this:
>>> user = User.objects.get(...)
>>> user.userprofile.user_phone
12345
You can also consider writing a custom User model, so that you can get rid of UserProfile.
Bonus tip: PositiveIntegerField is not the right field for a phone number. Leading zeroes have a meaning. Also, PositiveIntegerField have a maximum value. Use CharField instead.
Use a OneToOneField
To make it more direct, I'd make the UserProfile have a OneToOneField relationship with User, instead of a ForeignKey. Because this will mean that a given user can only have one profile.
class Location(models.Model):
user = models.OneToOneField(User)
In which case you can access it easier with location.user.userprofile.your_field
Using a custom MyUser model
If you want to make this even more direct, you could make a custom MyUser model that will contain both the fields from User and UserProfile.
It would go roughly like this:
from django.contrib.auth.models import AbstractBaseUser
class MyUser(AbstractBaseUser):
# Adding your custom fields
user_base = models.CharField(max_length=120, choices=LOCATION_CHOICES)
user_position = models.CharField(max_length=120)
user_phone = models.CharField(max_length=120)
slug = models.SlugField()
class Location(models.Model)
user = OneToOneField(MyUser) # Using your custom MyUser model
This allows a more direct access, e.g. location.user.user_phone instead of location.user.userprofile.user_phone
I've only provided pseudocode, please refer to Django documentation
Using a ForeignKey means you may have multiple profiles
In the other case where a user may have multiple user profiles, you then have the burden on you to select which profile to use to pull the relevant data from, because then the relationship would be user.userprofile_set, a set that you will have to filter/index to choose from.
Related
In my pervious question I asked how I can automatically save the user submitting the form. I found the form_valid method to be the best in that case. However in my models I also have a user profile model like this
models.py
....
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
title = models.CharField(max_length=24)
first_name = models.CharField(max_length=35)
last_name = models.CharField(max_length=35)
email = models.EmailField(max_length=64)
phone_number = models.CharField(max_length=12)
department = models.ForeignKey(Department,null=True,on_delete=models.SET_NULL)
supervisor = models.ForeignKey('self',blank=True,null=True,on_delete=models.SET_NULL)
...
As you can see I used the One to One method to make my UserProfile
As before in my models.py I have my reports model
...
class Report(models.Model):
id = models.UUIDField(primary_key=True,default=uuid.uuid1,editable=False)
department = models.ForeignKey(Company,null=True,on_delete=models.SET_NULL)
user= models.ForeignKey(User,on_delete=models.PROTECT)
submission_date= models.DateField(auto_now=True) #invisible to user
submission_time = models.TimeField(auto_now=True) #invisible to ,user
date = models.DateField(default=now,blank=False)
time = models.TimeField(default=now,blank=False,help_text="hh:mm:ss")
location = PlainLocationField()
building = models.ForeignKey(bld,null=True,on_delete=models.SET_NULL)
size = models.PositiveIntegerField()
notes = models.TextField(blank=True)
def __str__(self):
return f'{self.date} {self.time} ({self.department})
...
My question how I can make it so that the department field will load from the user profile? I would hope to eventually make it possible for users in the same department to be able to view and update each others Reports.
As before:
form.py
class ReportForm(forms.ModelForm):
class Meta:
model = Report
fields = '__all__'
location = PlainLocationField()
def redirect():
return redirect("Report")
views.py
class ReportCreate(LoginRequiredMixin,CreateView):
Template = "templates\reports\Report.html"
model = Report
fields = '__all__'
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.save()
return super(ReportCreate, self).form_valid(form)
def get_success_url(self):
return reverse('Report')
def get_absolute_url(self):
return reverse('Report', kwargs={'pk':self.pk})
I advise you to use related_name in your ForeignKeys. Set the department field of both models as following:
class Profile(models.Model):
...
department = models.ForeignKey(Department, null=True, on_delete=models.SET_NULL, related_name='profiles')
...
class Report(models.Model):
...
department = models.ForeignKey(Department, null=True, on_delete=models.SET_NULL, related_name='reports')
...
From now on, Department objects that are related to User.Profile you can access like that:
Department.profiles.all() # it returns QuerySet of all related to Department Profile objects
Department.reports.all() # it returns QuerySet of all related to Department Report objects
And you can use it in making QuerySet for user:
Report.objects.filter(department=self.request.user.profile.department)
# it returns all Report objects, that have exactly the same department as the user
Or using our new relationship:
department = self.request.user.profile.department
reports_for_user = department.reports.all()
But I can see one problem. You are using Company model for ForeignKey in Report. It has to be the same Department model for both Profile and Report models for such easy option to work. Also you definitely should not mix naming in single project. You can set relation with Company as another field:
company = models.ForeignKey(Company, null=True, on_delete=models.SET_NULL)
Here are some steps to help you autofill some fields:
Get the user from self.request.user. How to access current user in Django class based view
Get the profile: get user profile in django
Pass the required fields as context variables: https://www.geeksforgeeks.org/how-to-pass-additional-context-into-a-class-based-view-django/
Pass it into javascript. How can I pass my context variables to a javascript file in Django?
Set the value like this: Set the value of an input field
DONE!
I have a model using TimeStampedModel where created and updated is already integrated
class Universo(TimeStampedModel):
nombre = models.CharField('Universo', max_length=10)
class Meta:
verbose_name = 'Universo'
verbose_name_plural = 'Universos'
ordering = ['nombre']
def __str__(self):
return self.nombre
In the administrator view indicate the user who created and modified it
Image Admin
In template I can call its attributes with
{{u}} {{u.created}} {{u.modified}}
But I can't find how to bring the user I think I have made the modification to the template
You have to do a bit more to achieve what you want. I'll give the starting point:
You must first establish a relationship between your Universo model and Django user model.
from django.auth.models import User:
class Universo(TimeStampedModel):
nombre = models.CharField('Universo', max_length=10)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name = 'Universo'
verbose_name_plural = 'Universos'
ordering = ['nombre']
def __str__(self):
return self.nombre
You can use the admin login to use your superuser as the logged-in user. But, in your views, you need to include the logged-in user (from request.user) in the creation of the instance for your Univeso model.
Then, in your template, you can render who has created it by doing this:
{{ u.created_by }}
you can further probe the fields of created_by:
{{u.created_by.name}}
Modify your template like this:
{{u}} {{u.created}} {{u.modified}} {{u.created_by.username}}
You can use another of the fields like username,email or any other
This should give the user that modified it.
Hi there Im trying to retrieve a specific object from the related model so as to render data to my view specific to that particular object, in my case I have a custom user model and a related model called Seller.
Models
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
is_customer = models.BooleanField(default=False)
is_seller = models.BooleanField(default=False)
class Seller(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
store_name = models.CharField(max_length=120)
address = models.CharField(max_length=180)
phone = models.IntegerField(blank=True, null=True)
email = models.CharField( max_length=180, blank=True, null=True )
def __str__(self):
return self.store_name
View
#method_decorator( seller_required , name='dispatch')
class SellerDashBoardView(ListView):
model = Seller
template_name = 'seller_dashboard.html'
def get_context_data(self, *args, **kwargs):
user = CustomUser.seller_set.filter(store_name=self.request.user.username)
context = super(SellerDashBoardView, self).get_context_data( **kwargs)
context['products'] = Product.objects.filter(seller=user)[:6]
return context
This is because when you want to filter ManyToOne reverse Relation, you have to make exact the same query as you would've been done with a direct relation:
CustomUser.objects.filter(seller__store_name="Whole Foods")
# Note that would return a queryset not a single user!
# If you want a CustomUser object you will have to use either get or index the query
The doc example and explanations are provided here:
https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_one/
It is also better to use prefetch_related method to tell djano ORM that it does not have to make as many queries as number of related objects, that query should be done in 2 database queries instead of lenght of your related query:
CustomUser.objects.prefetch_related("seller_set").filter(seller__store_name="Whole Foods")
The doc link:
https://docs.djangoproject.com/en/3.1/ref/models/querysets/#prefetch-related
You probably would like to use ...seller_set.filter when you already got a CustomUser object. So if you want to filter its sellers you would use that:
...
user.seller_set.filter(store_name="Whole Foods")
That would provide you the Seller objects queryset filtered by a store name related to a specific user. Basically the same query as this:
Seller.objects.filter(user_pk=user.pk, store_name="Whole Foods")
I'm creating a simple contacts app in django and I want to allow everyone to have more than a single phone number.
My models.py looks like this:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
birthday = models.DateField(null=True, blank=True)
def __str__(self):
return ' '.join([self.first_name, self.last_name])
class Phone(models.Model):
phone_number = models.CharField(max_length=12)
description = models.CharField(max_length=25)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
def __str__(self):
return self.phone_number
I need to create a form where I can add the core contact info and as many phone numbers as wanted.
I have a ModelForm like
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = '__all__'
How do I create a phone_number field which allows to insert the phone number?
Here is a nice tutorial that is perhaps a bit more helpful than the documentation if you're struggling:
http://whoisnicoleharris.com/2015/01/06/implementing-django-formsets.html
It goes through using formsets which allow you to have and use more than one form of the same type on a page.
And also shows you a JQuery plugin that allows you to add and remove forms dynamically.
I am very new to Python and Django. I am trying to setup user model for different roles like Agents, Brokers, Company and Customer. Each of these roles can register with the site as a user. Then Agents and Brokers can have public facing profile pages.
Do I have to use custom user model or built in user model will work? I have additional properties like license, location, languages, photo etc for Agents and Brokers.
class MyUser(AbstractBaseUser):
AGENTS = 'AG'
BROKERS = 'BR'
COMPANY = 'CP'
CUSTOMER = 'CM'
ROLE_IN_CHOICES = (
(AGENTS, 'Agent'),
(BROKERS, 'Broker'),
(COMPANY, 'Company'),
(CUSTOMER, 'Customer'))
first_name = models.CharField(max_length=100, blank=True)
second_name = models.CharField(max_length=100, blank=True)
middle_name = models.CharField(max_length=100, blank=True)
dob = models.DateField(blank=True, null=True)
phone = models.CharField(max_length=10)
secondary_phone = models.CharField(max_length=10, blank=True, null=True)
......
#property
def is_agent(self):
return self.role in (self.AGENTS)
#property
def is_customer(self):
return self.role in (self.CUSTOMER)
#property
def is_broker(self):*
return self.role in (self.BROKER)
#property
def is_company(self):
return self.role in (self.COMPANY)
....
Can I not use base User model and achieve same? Am I on write track?
How do I create public facing pages for these roles (Agents, Brokers)?
This is my first attempt with Python and Django. I am using Django 1.7.7 with Python 3.4
You should extend from the Django User model instead and add the extra fields you need:
from django.contrib.auth.models import User as Auth_User
class User(Auth_User):
# add your extra fields here like roles, etc
phone = CharField(max_length=20, null=True, blank=True)
# add your extra functions
def extra_user_function(self):
return "This is an extra function"
This way you have your own fields and also the Django User fields...
After migrating, if you check the database, you'll have auth_user and your_app_user tables.
Just bare in mind that request.user will only give you the super fields... In order to get the extended class you'll need
User.objects.get(id=request.user.id)
The latest will only have the extra fields and its id will be the same as the auth.User if you don't add any auth_user by itself.
Attention: this is important!
Otherwise request.user.id and your_app.User.id don't match, therefore User.objects.get(id=request.user.id) won't work and you'll have to query the db to find the your_app.User.id
User.object.get(user_ptr_id = request.user.id)
Other things to consider
This will work:
# you_app.User objects gets vars from auth.User
user = User.objects.get(id=request.user.id)
first_name = user.first_name
But this won't work
# auth.User trying to get a function from your_app.User
user = request.user
user.extra_user_function()
So the User model could be something like this:
import os
from django.contrib.auth.models import User as Django_User
from django.db.models import CharField, ImageField
class User(Django_User):
phone = CharField(max_length=20, null=True, blank=True)
observations = CharField(max_length=2048, null=True, blank=True)
picture = ImageField(upload_to='users', default='default/avatar.jpg')
class Meta:
# adding extra permissions (default are: add_user, change_user, delete_user)
permissions = (
("access_user_list", "Can access user list"),
("access_user", "Can access user"),
)
ordering = ["-is_staff", "first_name"]
Instead of creating roles on the user, Django already has groups, so you should use them.
The groups follow the same logic:
from django.contrib.auth.models import Group as Auth_Group
from django.db import models
class Group(Auth_Group):
observations = models.CharField(max_length=2048, null=True, blank=True)
def get_users_in_group(self):
return self.user_set.filter(is_active=1).order_by('first_name', 'last_name')
def count_users_in_group(self):
return self.user_set.count()
def __unicode__(self):
return self.name
class Meta:
permissions = (
("access_group_list", "Can access group list"),
("access_group", "Can access group"),
)
ordering = ["name"]
You can clear / add users to a group:
user.groups.clear()
user.groups.add(group)
Clear / add permissions to the group:
group.permissions.clear()
group.permissions.add(permission)
There is also a decorator to check if a user has permissions
from django.contrib.auth.decorators import permission_required
#permission_required(("users.change_user","users.access_user",))
def your_view(request):
...
I've tried many things in the past, but I guess this is the way to go.
If you really need roles, and a user can have more than one role, mayb the best thing would be to create a model Role and add that to the user has a ManyToMany Field
roles = ManyToManyField(Role)
but you could do that with groups