Users to see only their objects in Django - django

So I have an application where users can create their own Companies. But what I want in the view is for them to see only their entries on the view. I have seen similar questions on this platform but they don't work as expected. Below is my code.
Models.Py
from django.contrib.auth.models import User
class Company (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
company_name = models.CharField(max_length=100, null=True)
mailing_address = models.CharField(max_length=100, null=True)
physical_address = models.CharField(max_length=100, null=True)
class Meta:
verbose_name_plural = "Companies"
def __str__(self):
return self.company_name
views.py
#login_required(login_url='login')
def company (request):
all_companies = Company.objects.filter(user=request.user)
count= Company.objects.all().count()
context = {'all_companies': all_companies, 'count': count}
return render(request, 'company/company.html', context)
forms.py
class CompanyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CompanyForm, self).__init__(*args, **kwargs)
self.fields['company_name'].widget.attrs = {'class': 'input',}
self.fields['date_created'].widget.attrs = {'class': 'input',}
self.fields['mailing_address'].widget.attrs = {'class': 'input',}
self.fields['physical_address'].widget.attrs = {'class': 'input',}
class Meta:
model = Company
fields = ('company_name', 'date_created', 'mailing_address', 'physical_address',)
The so largely this works to ensure that every user only sees the company they have created. However, I can successfully create the companies from the admin side but a glaring issue appears. I have to manually select users from the form field = users in the admin form as shown in the picture below, to be able to create and save companies. It is the same behaviour on the front end with the form. This doesn't look right.
How can I ensure a company automatically points to the owner (user) who created it, without having to manually force the form to choose the user.
admin page

If you want the user to be added automatically in Django admin, you can override the save_model of its corresponding ModelAdmin:
https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model
If you want it to be populated when users are creating companies using forms, you can set the user attribute like this:
# assuming request.user is available
company_form = form.save(commit=False)
company_form.user = request.user
company_form.save()

Since, user is the foreign key. You can take advantage of
'formfield_for_foreignkey' method in the ModelAdmin class.
This method gets executed for the foreign fields declared in the model. Here, we can check whether it has been executed for the user or not if yes, then we can prepopulate its value. You can customize the admin form by creating ModelAdmin class in admin.py for the Company model
#admin.register(Company)
class CompanyAdmin(admin.ModelAdmin):
form = CompanyForm
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'user':
kwargs['initial'] = request.user.id
return db_field.formfield(**kwargs)
return super(CompanyAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
In the ModelAdmin class you can specify the form class for further customizations.
Note, this will only prepopulate the value, the value of the user can be changed in the form. To avoid this, you can make user field uneditable and readonly field.

So I finally found the solution, at least for the user field in the Company form.
This gives a clear way of doing this: https://docs.djangoproject.com/en/4.0/topics/class-based-views/generic-editing/#models-and-request-user
In views.py: I added form.instance for the field user to ensure it picks the current user and feeds it in the form.
def company_form (request):
form = CompanyForm()
if request.method == 'POST':
# Request files helps upload other files such as images
form = CompanyForm(request.POST, request.FILES)
#This automatically inserts the user without exposing the form field
form.instance.user = request.user
if form.is_valid():
form.save()
return redirect('company')
Then I modified the model field for the user to ensure it is not editable.
models.py
class Company (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
This ensured no one can edit the user including the admin. That ideally solves 90 percent of my issues.
Appreciate everyone's help on this.

Related

How to link Django User model to certain Post models

So let's say I have a model called "Post" that looks like this:
class Post(models.Model):
title = models.CharField(max_length=50)
body = models.TextField()
def __str__(self):
return self.title
now, say I have an option for users to create a Post on my site. How could I alter the standard User model and give it a characteristic containing all of the posts that that user has created. For example, say we have a user who has created a post. In the interactive shell that Django has, I could enter "user.posts" and It would pull up all the posts that that user has created. How could I go about this?
You can do this by adding users as a foreign key to your Posts model. Try this after the "body" line and migrating.
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
To query relevant posts in a view you could then filter your Posts model by user like this:
user_posts = Posts.objects.filter(user=request.user)
You add a ForeignKey from Post to the user model:
from django.conf import settings
class Post(models.Model):
title = models.CharField(max_length=50)
body = models.TextField()
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='posts',
editable=False
)
In a view, you can link the logged in user with a post with:
from django.contrib.auth.decorators import login_required
#login_required
def create_post(request):
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('name-of-some-view')
else:
from = PostForm()
return render(request, 'some_template.html', {'form': form})
The related_name parameter specifies the name of the relation in reverse. So the manager to access the Post objects related to a User object. So if you have a user object myuser, you can access the posts with:
myuser.posts.all()

Django taggit how to record id of user that added tag

I have Django app using django-taggit module. My work flow process is to add a photo, then come back to photo to assign tags using a photo_edit view, photo_edit form and photo_edit.html template as shown below.
The app successfully tags the photo, using the 'tags': TagWidget() in the form and template. This works fine, and it can save new, modified or deleted tags against that photo.
But I want to be able to record the user id of person adding tag in the django taggit model database table. django taggit doesn't have this feature.
To get started, I have successfully added a new user field to the class TagBase(models.Model): using settings.AUTH_USER_MODEL to refer to user object which creates new user_id column in the taggit_tags table when makemigrations is run.
Now I need to figure out how to get current user id to populate that new user_id column when adding or editing tagged object tags.
I have tried doing this in both my app view and django taggit model. Ideally, since this would be a default feature, the user id is saved in django taggit model.
In django taggit model I have tried adding self.user = get_user_model().objects.get(id=request.user.id) to the TagBase def save function. But of course it doesn't know what request is. I also tried creating new function in the TagBase model class as below, and calling it from the def save function but it returns nothing.
def get_user(request):
user = request.user
print('MODEL request user', request.user)
return user
As a test, I replaced request.user.id with hard coded id eg self.user = get_user_model().objects.get(id=1) which actually does write 1 to the taggit_tag table user_id field for that tag.
I tried doing this in the app view too. For example, I have tried photo.tag.user_id = request.user.id before save_m2m() which didn't work. It is not clear what syntax, if any, is available to save related model fields. I believe I would have to modify the form TagWidget() to include user_id field so save_2m2() would save it, but want to avoid modifying django-taggit too much.
I have searched exhaustively and have not found anything specific to Django and django-taggit. Perhaps someone could help from Python perspective!
Photo edit form
class PhotoEditForm(ModelForm):
#filename = forms.ImageField()
class Meta:
model = Photo
fields = (
'tags',
)
widgets = {
'tags': TagWidget(),
}
Photo edit view - ideally i could save request.user before form.save_m2m() something like photo.taggit_tag.user_id = user.id. Is there some syntax that would work here?
def photo_edit(request, photo_id):
photo = Photo.objects.get(id=int(photo_id))
user = get_user_model().objects.get(id=request.user.id)
photo_tags = Tag.objects.filter(photo=photo.id).order_by('name')
if request.method == "POST":
form = PhotoEditForm(request.POST, instance=photo)
if form.is_valid():
photo = form.save(commit=False)
photo.modified = timezone.now()
photo = form.save()
#save tags if any
form.save_m2m()
return HttpResponseRedirect(reverse('photo', kwargs={ 'photo_id': photo.id,}))
else:
form = PhotoEditForm(instance=photo)
context = {
'photo_form': form,
'photo': photo,
'photo_tags': photo_tags,
'title': 'Edit Mode',
}
return render(
request,
'photo_edit.html',
context,
)
Photo model
class Photo(models.Model):
filename = CloudinaryField('image')
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=True)
inactive = models.BooleanField(default=False, choices=INACTIVE_CHOICES)
created = models.DateTimeField(blank=True, null=True)
modified = models.DateTimeField(blank=True, null=True)
tags = TaggableManager()
class Meta:
managed = True
db_table = 'photo'
verbose_name_plural = 'photos'
def __str__(self):
return str(self.filename)
I did make slight modification of django taggit model to include user_id field. Ideally i could set this user_id to request.user as default value but haven't seen anything yet that looks simple or easy.
class TagBase(models.Model):
name = models.CharField(verbose_name=_("Name"), unique=True, max_length=100)
slug = models.SlugField(verbose_name=_("Slug"), unique=True, max_length=100)
# added user to model which created `user_id` column on `taggit_tags` table
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=True)
def __str__(self):
return self.name
def __gt__(self, other):
return self.name.lower() > other.name.lower()
def __lt__(self, other):
return self.name.lower() < other.name.lower()
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.user = get_user_model().objects.get(id=1)
if self._state.adding and not self.slug:
self.slug = self.slugify(self.name)
using = kwargs.get("using") or router.db_for_write(
type(self), instance=self
)
You may want to look at django-crum: Django-CRUM (Current Request User Middleware) captures the current request and user in thread local storage.
I used it when tackling a similar issue. It allows you to access the current user without the request object having to be passed directly:
https://django-crum.readthedocs.io/en/stable/

Django: set user creating a post (creator of a row) automatically as creator/author [duplicate]

I'd like to do something like this:
class Task(models.Model):
...
created_by = models.ForeignKey(User, **default=[LoggedInUser]** blank=True, null=True, related_name='created_by')
Is this possible? I couldn't find what's the proper way to get the logged in user, apart from doing request.user, in a view, which doesn't seem to work here.
PS_ I realise I could initialize the Model data by other means, but I think this is the cleanest way.
If you want to achieve this within the admin interface, you can use the save_model method. See below an example:
class List(models.Model):
title = models.CharField(max_length=64)
author = models.ForeignKey(User)
class ListAdmin(admin.ModelAdmin):
fields = ('title',)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
No, you can't do it this way. Django (and Python) has pretty much zero global values, and that's a Good Thing(tm). Normally you get the current user in the view(request) with request.user. You can then pass that as a param to various methods/functions, but trying to set a global user will only lead to tears in a multi-threaded environment.
There should be a bumper sticker that says, Globals are Evil. This will give you a good idea about my Number One problem with PHP.
SOLVED:
I will use an example, but the important part is the funciton on the views.py.
User is automatically available by django.
Note the 'autor' model field has a ForeignKey to the 'User'.
In the 'def form_valid' below I assign the currently logged in user as the default value.
If this is your model:
class ProspectoAccion(models.Model):
"""
Model representing a comment against a blog post.
"""
descripcion = models.TextField(max_length=1000)
autor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
accion_date = models.DateTimeField(auto_now_add=True)
prospecto= models.ForeignKey(Prospecto, on_delete=models.CASCADE)
tipo_accion = models.ForeignKey('Accion', on_delete=models.SET_NULL, null=True)
And you have a class based view, do the following:
class ProspectoAccionCreate(LoginRequiredMixin, CreateView):
"""
Form for adding una acción. Requires login (despues poner)
"""
model = ProspectoAccion
fields = ['tipo_accion','descripcion',]
def form_valid(self, form):
#Add logged-in user as autor of comment THIS IS THE KEY TO THE SOLUTION
form.instance.autor = self.request.user
# Call super-class form validation behaviour
return super(ProspectoAccionCreate, self).form_valid(form)
HERE IS AN EXAMPLE FROM THE DOCUMENTATION:
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
If use ModelForm, the following will fill a default value for a special field. such as, owner filed is a charfield for user's name
def fillview(request):
instance = YourModel(owner=request.user.username)
form = YourModelForm(instance=instance)
if request.method == 'POST':
form = YourModelForm(request.POST, request.FILES)
if form.is_valid():
pass
return render(request, 'success.html')
return render(request, 'fill.html', {'form': form})
When logged in, you could see owner filed is current user's name.
By default, Django already creates a "created_by" attribute. You don't need to create your own.
If you nonetheless need to save this information separately to, let's say, have the possibility to change the user later on without affecting the original creator value, then you could override the save function to retrieve the value that Django assigns by default to "created_user":
class Application(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='applications', editable=False, null=True)
...
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.user:
self.user = self.created_by
super(Application, self).save(*args, **kwargs)

How to assign currently logged in user as default value for a model field?

I'd like to do something like this:
class Task(models.Model):
...
created_by = models.ForeignKey(User, **default=[LoggedInUser]** blank=True, null=True, related_name='created_by')
Is this possible? I couldn't find what's the proper way to get the logged in user, apart from doing request.user, in a view, which doesn't seem to work here.
PS_ I realise I could initialize the Model data by other means, but I think this is the cleanest way.
If you want to achieve this within the admin interface, you can use the save_model method. See below an example:
class List(models.Model):
title = models.CharField(max_length=64)
author = models.ForeignKey(User)
class ListAdmin(admin.ModelAdmin):
fields = ('title',)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
No, you can't do it this way. Django (and Python) has pretty much zero global values, and that's a Good Thing(tm). Normally you get the current user in the view(request) with request.user. You can then pass that as a param to various methods/functions, but trying to set a global user will only lead to tears in a multi-threaded environment.
There should be a bumper sticker that says, Globals are Evil. This will give you a good idea about my Number One problem with PHP.
SOLVED:
I will use an example, but the important part is the funciton on the views.py.
User is automatically available by django.
Note the 'autor' model field has a ForeignKey to the 'User'.
In the 'def form_valid' below I assign the currently logged in user as the default value.
If this is your model:
class ProspectoAccion(models.Model):
"""
Model representing a comment against a blog post.
"""
descripcion = models.TextField(max_length=1000)
autor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
accion_date = models.DateTimeField(auto_now_add=True)
prospecto= models.ForeignKey(Prospecto, on_delete=models.CASCADE)
tipo_accion = models.ForeignKey('Accion', on_delete=models.SET_NULL, null=True)
And you have a class based view, do the following:
class ProspectoAccionCreate(LoginRequiredMixin, CreateView):
"""
Form for adding una acción. Requires login (despues poner)
"""
model = ProspectoAccion
fields = ['tipo_accion','descripcion',]
def form_valid(self, form):
#Add logged-in user as autor of comment THIS IS THE KEY TO THE SOLUTION
form.instance.autor = self.request.user
# Call super-class form validation behaviour
return super(ProspectoAccionCreate, self).form_valid(form)
HERE IS AN EXAMPLE FROM THE DOCUMENTATION:
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
If use ModelForm, the following will fill a default value for a special field. such as, owner filed is a charfield for user's name
def fillview(request):
instance = YourModel(owner=request.user.username)
form = YourModelForm(instance=instance)
if request.method == 'POST':
form = YourModelForm(request.POST, request.FILES)
if form.is_valid():
pass
return render(request, 'success.html')
return render(request, 'fill.html', {'form': form})
When logged in, you could see owner filed is current user's name.
By default, Django already creates a "created_by" attribute. You don't need to create your own.
If you nonetheless need to save this information separately to, let's say, have the possibility to change the user later on without affecting the original creator value, then you could override the save function to retrieve the value that Django assigns by default to "created_user":
class Application(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='applications', editable=False, null=True)
...
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.user:
self.user = self.created_by
super(Application, self).save(*args, **kwargs)

Django Initial for a ManyToMany Field

I have a form that edits an instance of my model. I would like to use the form to pass all the values as hidden with an inital values of username defaulting to the logged in user so that it becomes a subscribe form. The problem is that the normal initial={'field':value} doesn't seem to work for manytomany fields. how do i go about it?
my views.py
#login_required
def event_view(request,eventID):
user = UserProfile.objects.get(pk=request.session['_auth_user_id'])
event = events.objects.get(eventID = eventID)
if request.method == 'POST':
form = eventsSusbcribeForm( request.POST,instance=event)
if form.is_valid():
form.save()
return HttpResponseRedirect('/events/')
else:
form = eventsSusbcribeForm(instance=event)
return render_to_response('event_view.html', {'user':user,'event':event, 'form':form},context_instance = RequestContext( request ))
my forms.py
class eventsSusbcribeForm(forms.ModelForm):
eventposter = forms.ModelChoiceField(queryset=UserProfile.objects.all(), widget=forms.HiddenInput())
details = forms.CharField(widget=forms.Textarea(attrs={'cols':'50', 'rows':'5'}),label='Enter Event Description here')
date = forms.DateField(widget=SelectDateWidget())
class Meta:
model = events
exclude = ('deleted')
def __init__(self, *args, **kwargs):
super(eventsSusbcribeForm, self).__init__(*args, **kwargs)
self.fields['username'].initial = (user.id for user in UserProfile.objects.filter())
my models.py
class events(models.Model):
eventName = models.CharField(max_length=100)
eventID = models.AutoField(primary_key=True)
details = models.TextField()
attendanceFee = models.FloatField(max_length=99)
date = models.DateField()
username = models.ManyToManyField(UserProfile, related_name='user', blank=True)
eventposter = models.ForeignKey(UserProfile, related_name='event_poster')
deleted = models.BooleanField()
def __unicode__(self):
return u'%s' % (self.eventName)
Can you post your Event model? It's too hard to guess what you are trying to do without that. I have to assume a few things without it, so I'm sorry if I'm wrong.
First off, I'm guessing that you should not be using an Event ModelForm for the EventSubscriptionForm. That doesn't really make sense. Hopefully, you created a through class for Event and User, so in your Event model, you have something like
subscriber_users = models.ManyToManyField(User, through="Subscription")
and
class Subscription(models.Model):
user = models.ForeignKey(User, related_name="events",)
event = models.ForeignKey(Event, related_name="subscribers")
Then you can use a Subscription ModelForm.
Is there any reason you're using eventID instead of the django idiom, event_id? You should also import your Event and EventSubcribeForm classes with Pythonic casing. One very important thing is that you should be linking everything to User and not UserProfile.
Technically, it makes more sense to set initial in the view rather than the form init, because you would have to pass request.user to init anyway.
I think you should try this for your view...
#login_required
def event_view(request, event_id=None):
user = request.user.get_profile()
event = Event.objects.get(id=event_id)
initial = {'user': request.user}
form = EventSubcriptionForm(request.POST or None, instance=event, initial=initial)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('event_list'))
return render_to_response('event_view.html', {
'event': event,
'form': form
}, context_instance = RequestContext(request))
A few notes
use request.user.get_profile() for the current user's profile object
you can use request.POST or None to avoid the request.method cases
always use named urls so you can reverse on names instead of hard-coding urls into views
if you want user in your template context, just setup a context processor (see pinax for example on how to do this) instead of passing it in every single view. You can always use request.user also.
Keep in mind that this code will only work if you have that through class setup like I said and you use a form like
class EventSubcriptionForm(forms.ModelForm):
class Meta:
model = Subscription
exclude = ('event')
EDIT
Thanks a bunch for the ups. I'm not new to django, but somehow very new to SO.
Okay, you should really read some of the PEPs about Python conventions http://www.python.org/dev/peps/pep-0008/ or some SO posts about it What is the naming convention in Python for variable and function names?.
Here's what I recommend for your event app models.py:
class Event(models.Model):
name = models.CharField(max_length=100)
details = models.TextField()
attendance_fee = models.FloatField(max_length=99)
date = models.DateField()
poster = models.ForeignKey(User, related_name='events_posted')
deleted = models.BooleanField()
attendee_users = models.ManyToManyField(User, through="Attendance")
def __unicode__(self):
return self.name
class Attendance(models.Model):
user = models.ForeignKey(User, related_name="events",)
event = models.ForeignKey(Event, related_name="attendees")
Notes
The name of a class is capitalized and singular. You are not describing events, you are the blueprint for an Event.
you never need the name of the class in its attributes, i.e. event_name can just be name.
all variables are lowercase_and_underscored
always link to User, not your profile model. A lot of django code expects this.
So now you can access the users attending the event with event.attendees.
I found this while trying to set defaults for the manytomany. I didn't want to add a through table.
based on the view Casey posted, but adding the user in the manytomany relation.
for the initial post:
#login_required
def event_view(request, event_id=None):
user = request.user.get_profile()
event = Event.objects.get(id=event_id)
initial = {'user': request.user, 'username': [ request.user.id, ] } # makes the poster also an attendee
form = EventSubcriptionForm(request.POST or None, instance=event, initial=initial)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('event_list'))
return render_to_response('event_view.html', {
'event': event,
'form': form
}, context_instance = RequestContext(request))
updated version:
#login_required
def event_view(request, event_id=None):
user = request.user.get_profile()
event = Event.objects.get(id=event_id)
initial = {'user': request.user, 'subscriber_users': [ request.user.id, ] } # makes the poster also an subscriber
form = EventSubcriptionForm(request.POST or None, instance=event, initial=initial)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('event_list'))
return render_to_response('event_view.html', {
'event': event,
'form': form
}, context_instance = RequestContext(request))