I have my user table in django, and to differ all the users I created two tables, (Teacher and Student).
Both tables are getting an fk from user
So, in order to make authorization how do I check if one's user is in a certain table.
I need to check it this way
def test_func(self):
return self.request.user.check..if..it..exists..in..table
My models are like this.
class Teacher(models.Model):
User = models.OneToOneField(settings.AUTH_USER_MODEL)
This depends on how your models are set up.
If your Teacher model looks something like this;
class Teacher(models.Model):
user = models.ForeignKey(User)
Then you should be able to check if the user is a teacher by using the implicit backref;
self.request.user.teacher_set.exists()
As the question has been updated to show that the model is slightly different than I anticipated, here is an update.
class Teacher(models.Model):
user = models.OneToOneField(User)
Which means that the backref will be a little different.
hasattr(self.request.user, "teacher")
As you've mentioned that you are doing this inside a django template, I'm pretty sure that the following will work:
{% if user.teacher %}
Since you haven't posted your models, I am giving you a rough idea how to do it.
in your views.py -
from .models import Teacher,Student
def test_func(request):
user = request.user
if (Teacher.objects.filter(user=user).count() > 0) or (Student.objects.filter(user=user).count > 0):
#do your stuffs here..
One way is to query both tables:
teacher = Teacher.objects.filter(user=self.request.user)
student = Student.objects.filter(user=self.request.user)
if teacher or student:
# do what you want.
If you put in your relation the argument "related_name" you can do it using inverse relationship
class SomeTable(models.Model):
user = models.ForeignKey(
User, #Your user model or Django one
verbose_name = "User",
related_name = "inverse_relation_name"
)
Then you have to call using keyword arguments for the filters:
SomeTable.inverse_relation_name.filter(id=self.request.user.id) #You will get a queryset
Or
SomeTable.inverse_relation_name.get(id=self.request.user.id) # You will get the object or a exception
Related
For my app, I want to add one extra ManyToManyField to the default User model (django.contrib.auth.models.User). This extra field is called 'favorites' and the posts favorited by a user should go there. This is what I have:
class Favorite(models.Model):
user = models.OneToOneField(User, related_name='favorites', on_delete=models.CASCADE)
favorites = models.ManyToManyField(Recipe, related_name='favorited_by')
This is what I get when trying to add to 'favorites' from the shell.
# imported Recipe, Favorite, User(default)
>>> recipe1 = Recipe.objects.all()[0]
>>> me = User.objects.all()[0]
>>> me.favorites.add(recipe1)
django.contrib.auth.models.User.favorites.RelatedObjectDoesNotExist: User has no favorites.
# Just checking if the the User object, me, has a 'favorites' attribute
>>> 'favorites' in dir(me)
True
What is the correct way to add a Recipe object to this 'favorites' field?
For more reference, I did something similar how I handled Friendships between users, but it was a bit simpler since I wasn't extending the User model. The code for that is below and works fine:
class Friend(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='owner', null=True, on_delete=models.CASCADE)
#classmethod
def make_friend(cls, current_user, new_friend):
friend, created = cls.objects.get_or_create(
current_user=current_user
)
friend.users.add(new_friend)
#classmethod
def lose_friend(cls, current_user, new_friend):
friend, created = cls.objects.get_or_create(
current_user=current_user
)
friend.users.remove(new_friend)
Resolved. My solution is below, but I'm not sure if this is good practice.
django.contrib.auth.models.User.favorites.RelatedObjectDoesNotExist: User has no favorites.
The User model may have the 'favorites' field, but I needed to actually fill it with a 'Favorite' object. I did this by writing a function in my views.py:
def add_favorite(request, pk):
# Check if the user has a favorites field. If not create one and add. If yes, just add
user_favorites, created = Favorite.objects.get_or_create(
user=request.user
)
recipe = get_object_or_404(Recipe, pk=pk)
user_favorites.favorites.add(recipe)
This seems to work and I can access a user's favorites now, but I maybe this isn't good practice. With my method, new models that are created do not have a 'Favorite' object within it. That will only get created when a user decides to add a favorite recipe and the above view will create one if it doesn't already exist.
I don't want to extend my userprofile.
I made a new model with name, contact and email.
The problem :
With createview the user is able to create multiple instances of the user_info model.
Is there any chance we can limit user to make only one user_info and update the same everytime.
models.py
class user_info(models.Model):
booked_by = models.CharField(max_length=100)
Name = models.CharField(max_length=40)
contact = models.IntegerField()
email = models.EmailField()
views.py
class user_info_create(LoginRequiredMixin,CreateView):
login_url = 'Mel:user_login'
form_class = user_infoform
template_name = 'Mel/user_info_form.html'
def form_valid(self, form):
form.instance.booked_by = self.request.user
return super(user_info_create, self).form_valid(form)
class user_info_detail(LoginRequiredMixin,DetailView):
login_url = 'Mel:user_login'
model = user_info
context_object_name = "book"
def get_queryset(self):
return user_info.objects.filter(booked_by=self.request.user)
As mentioned by #vorujack You need to create a OneToOne relationship between user_info and your user model. The R and RDBMS stands for Relations. So you need to build relationships between models. At the moment your system doesn't have any relation between user and the profile. However, the correct syntax is
booked_by = models.OneToOneField(User)
Then you need to do
python manage.py makemigrations
python manage.py migrate
if you already have duplicate entries in the table, the second step will fail. In that case you need to clear out the duplicates and run it again. If you have invalid entries in that table, the migration will still fail. So if you don't have any critical data, you might in fact want to clear out the whole table before you do this.
Another point worth noting.
https://legacy.python.org/dev/peps/pep-0008/#id39
Class Names
Class names should normally use the CapWords convention.
The naming convention for functions may be used instead in cases where the >interface is documented and used primarily as a callable.
Note that there is a separate convention for builtin names: most builtin names are single words (or two words run together), with the CapWords convention used only for exception names and builtin constants.
So your classes should really be UserInfo and UserInfoCreate
you can make a relation to your user model like this:
class user_info(models.Model):
booked_by = models.OneToOneField(User)
Name = models.CharField(max_length=40)
contact = models.IntegerField()
email = models.EmailField()
on create view set booked_by field with current user. with this change if user want to create multiple user_info it raised exception and no user_info inserted
Thank you vorujack and e4c5.
Just by doing OneToOneField is not solving the entire problem.
The below code really solved my problem.
If its not correct or can be done in a better way please let me know.
model.py
booked_by = models.OneToOneField(User)
views.py
class user_RedirectView(LoginRequiredMixin,RedirectView):
def get_redirect_url(self):
if user_info.objects.filter(booked_by=self.request.user).exists():
return reverse('Mel:user_update')
else:
return reverse('Mel:user_info_create')
Hey so I am making a color scheme posting site where people can register and post color schemes they come up with. So far everything is working great, the only thing I have left to do is add a "Like Post" feature. I'm wondering what the best way to implement this would be.
I have two ideas on how this could be done, the first is add an additional field to both the ColorSet (posts) and the User models (for the user model I would set up a new model with a OneToOne relationship to add onto the User model) which would record users that have each single post, and which posts each user has liked to keep track of everything.
So this could look something like this:
from django.db import models
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class ColorSet(models.Model):
user = models.ForeignKey(User,related_name='colorset')
published_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=50,blank=False)
color_one = models.CharField(max_length=6,blank=False,default='cccccc')
color_two = models.CharField(max_length=6,blank=False,default='ffffff')
color_three = models.CharField(max_length=6,blank=False,default='e5e5e5')
color_four = models.CharField(max_length=6,blank=False,default='f0f0f0')
color_five = models.CharField(max_length=6,blank=False,default='bababa')
liked_by = models.IntegerField(blank=True)
def publish(self):
self.save()
def get_absolute_url(self):
return reverse('index')
def __str__(self):
return self.name
user model:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserStats(models.Model):
user = models.OneToOneField(User)
liked_sets = models.IntegerField(blank=True)
def __str__(self):
return self.user.username
In this first option I would have the new model fields (liked_sets and liked_by) be equal to lists containing the ok's of all the Color Sets each user has liked and all the users who have liked each post respectively.
The other way that I'm thinking about would be to just create an entirely new model that tracks the likes for each color set post (not totally sure how this model would look yet exactly).
Aside from which is easier, I wondering which makes more sense from a technical standpoint? Will one of these two options take up more space or create heavier server load?
Thanks for the help.
As far as I understand your problem, it can be broken in two parts.
Maintaining the total number of likes on a model ColorSet.
Keeping the track of all those users who liked a single instance of ColorSet.
Now if I understand your problem correctly(correct me if I'm wrong), when you say:
new model fields (liked_sets and liked_by) be equal to lists containing the ok's of all the Color Sets each user has liked and all the users who have liked each post respectively.
you intend to create a field in your database which would simply store a list of pks of all the people who've liked a ColorSet model instance. Even if you don't intend to do that, still an IntegerField to store such information is(in my humble opinion) somewhat wrong.
Now why you wouldn't want to do that? It's because relational databases are made to recognize the relations between tuples of information and enhance the processing by creating relations. That is why we use relations like OneToOneField and ForeignKey. They make the processing way faster. If we were to simply store the pk values in a Field, further search them in our database to retrieve information, that would be something really slow.
Now I suppose what you are looking for is ManyToManyField.
In your problem, you will simply map the ManyToManyField it to the User model.
It would look something like:
class ColorSet(models.Model):
user = models.ForeignKey(User,related_name='colorset')
published_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=50,blank=False)
color_one = models.CharField(max_length=6,blank=False,default='cccccc')
color_two = models.CharField(max_length=6,blank=False,default='ffffff')
color_three = models.CharField(max_length=6,blank=False,default='e5e5e5')
color_four = models.CharField(max_length=6,blank=False,default='f0f0f0')
color_five = models.CharField(max_length=6,blank=False,default='bababa')
liked_by = models.IntegerField(blank=True)
#add a simple ManyToManyField which will hold all the users who liked this colorset
likers = models.ManyToManyField(User , related_name = 'liked_colorsets')
def publish(self):
self.save()
def get_absolute_url(self):
return reverse('index')
def __str__(self):
return self.name
and remove your UserStats model to
Now use the following code outline structure to access the information from the database.
1) To get the ColorSets liked by a User:
#obtain any user model object; for example: user_object = User.objects.get(...)
user_object.liked_colorsets.all()
#a queryset with all the liked colorsets is returned.
2) To get the Users who liked a ColorSet:
#obtain any colorset model object; for example: colorset_object = ColorSet.objects.get(...)
colorset_object.likers.all()
#a queryset with all the Users who liked this colorset is returned.
One more thing that I would like to add here. After a User likes a ColorSet, you would obviously want to add this User to the likers field in your ColorSet model(and increment the liked_by field; I assume you'll manage that). To add a User in the likers field:
#obtain any colorset model object; for example: colorset_object = ColorSet.objects.get(...)
#obtain the user model object of the user who liked this colorset in user_object
#and do
colorset_object.likers.add(user_object)
Read more about adding the models in ManyToManyField here in docs.
Hope this helps. Thanks.
I need to implement the following:
The user shall be presented with a form that will have a drop down choice menu consisting of property names. There are two types of properties: general properties, i.e. properties common for all users and custom properties, i.e. properties that each user has defined prior to that. The models would look something like that:
class GeneralPropertyName(models.Model):
name = models.CharField(max_length=20)
class CustomPropertyName(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=20)
The drop down menu should have all general properties and only those custom properties that pertain to the user.
First question: how to define such a model?
I need to: 1. somehow unify both properties, 2. take only those items from CustomPropertyName that pertain to the user
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(GeneralPropertyName) ??UNIFY??? ForeignKey(CustomPropertyName)
Second, is there anything special that needs to be done with ModelForm?
class SpecDataForm(ModelForm):
class Meta:
model = SpecData
And the 3rd question is what needs to be done in the view? I will need to use inline formsets since I will have a few dynamic forms like that.
def index(request):
user = User.objects.get(username=request.user.username)
specdataFormSet = inlineformset_factory(User, SpecData, form=SpecDataForm, extra=30)
...
specdata_formset = specdataFormSet(instance=user, prefix='specdata_set')
...
Thanks.
EDIT: Adjusted juliocesar's suggestion to include formsets. Somehow I am getting the following error message: Cannot resolve keyword 'property' into field. Choices are: id, name, selection_title, user
def index(request):
user = User.objects.get(username=request.user.username)
user_specdata_form = UserSpecDataForm(user=user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
You can use a GenericForeignKey to handle it, but you still need more to solve your further questions about forms and view.
I have made an example of how you solve your problem (logged user can select from General properties and his Custom properties, non-logged user only can select General properties). I used model inheritance for the properties (In your sample code it seems that a CustomPropertyName is a PropertyName with other fields). I think inheritance is an easier and a more basic concept than ContentTypes and it fits to your needs.
NOTE: I remove some code like imports to simplify the code.
1) models.py file:
class PropertyName(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class CustomPropertyName(PropertyName): # <-- Inheritance!!
user = models.ForeignKey(User)
def __unicode__(self):
return self.name
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = models.ForeignKey(PropertyName)
NOTES: The field SpecData.property points to PropertyName since all properties are saved in the PropertyName's database table.
2) forms.py file:
from django import forms
from django.db.models import Q
from models import SpecData, PropertyName
def UserSpecDataForm(user=None):
UserPropertiesQueryset = PropertyName.objects.filter(Q(custompropertyname__user=None) | Q(custompropertyname__user__id=user.id))
class SpecDataForm(forms.ModelForm):
property = forms.ModelChoiceField(queryset=UserPropertiesQueryset)
class Meta:
model = SpecData
exclude = ('user',)
return SpecDataForm
NOTES: The trick here is to generate the form SpecDataForm dynamically, by filtering properties according the user specified in the parameter.
3) views.py file:
from forms import UserSpecDataForm
def index(request):
if request.POST:
form = UserSpecDataForm(request.user)(request.POST) # instance=user
if form.is_valid():
spec_data = form.save(commit=False)
spec_data.user = request.user
spec_data.save()
else:
form = UserSpecDataForm(request.user)()
return render_to_response('properties.html', {'form': form}, context_instance=RequestContext(request))
NOTES: Nothing special here, just a call to form.UserSpecDataForm(request.user) that returns the form class and then instantiate. Also setted the logged-in user to the object returned on save since It was excluded in the form to not show in front-end.
Following this basic example you can do the same with formsets if you need it.
UPDATE:
Formset can be used by adding following code to the view:
user_specdata_form = UserSpecDataForm(user=request.user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
The complete project sample can be downloaded from http://ge.tt/904Wg7O1/v/0
Hope this helps
1a) have you looked into django's ContentType framework this will allow you to have generic foreign keys and you can put restrictions on what types of models are acceptable to store in.
1b) I think that the validation for accepting what type of foreign key is acceptable shouldn't be in your model but should be part of your form validation before saving.
2) If you do use a model form you're going to have to define your own custom widget for the propery field. This means you're probably going to have to write you're own render function to render the html from the field. You should also define your own validation function on the form to make sure that only the appropriate data is acceptable to save.
3) I don't think you'll have to do anything you aren't already doing in the views
Use GenericForeignKey:
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
property = GenericForeignKey('content_type', 'object_id')
You can use this to combine the two fields(type & id) into a single choice field.
One way is that you have only one model, make user nullable:
class PropertyName(models.Model):
user = models.ForeignKey(User, null=True, blank=True)
name = models.CharField(max_length=20)
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(PropertyName)
So, if user is not set, it is a general property. If it is set, it is related to this user.
However, please note that if you need unique property names, that NULL != NULL.
Of course, the suggested GenericForeignKey solution is better for some cases.
Also, you can easily make the normal (non-model) form with that you describe and separate form logic from model logic.
I want the ability to let users indicate what countries they have visited.. my models.py looks something like this:
class User(models.Model):
name = models.CharField(max_length=50)
countries = models.ManyToManyField(Countries)
class Countries(models.Model):
#This is where I don't know what to do.
#The end goal is for the user to be able to check off what countries he/she has visited
You would create the relationship the other way around
class User(models.Model):
name = models.CharField(max_length=50)
class Countries(models.Model):
user = models.ForeignKey(User)
If you are using django's built in User stuff then you only need this.
from django.contrib.auth.models import User
class Countries(models.Model):
user = models.ForeignKey(User)
Relation fields already generate an attribute on the other model for the reverse relation unless explicitly disabled.
You're fine as is w/r/t the ManyToManyField.
Now you'll want to create a form for this model to allow the checking-off to be done by your users.