I have two models Exercise and Area, Area has a manytomanyfield back to Exercise:
class Exercise(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
class Area(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
exercise = models.ManyToManyField(Exercise, blank=True)
I want the user to be able to create a new exercise that automatically attaches to its specific area, and also passes in request.user. Here is my view:
from .forms import ExerciseForm as e_form
class ExerciseFormView(CreateView):
form_class = e_form
success_url = '/'
def form_valid(self, form):
form.save()
area = Area.objects.get(pk=self.kwargs["area_id"])
new_ex = Exercise.objects.latest('id')
new_ex.user = self.request.user
area.exercise.add(new_ex)
form.instance.user = self.request.user
return JsonResponse({'exercise': model_to_dict(new_ex)}, status=200)
And its registered url:
path('exercise-add-new/<int:area_id>/', ExerciseFormView.as_view(), name='exercise_add_new'),
I should add that this used to work until I recently added user as a field to the Exercise model, and new_ex.user = self.request.user to the view. Now when I try to submit the form nothing happens. No errors, and no objects are created. I have tried new_ex.user = self.request.user and form.instance.user = self.request.user and still nothing happens.
Btw my user attributes are a ForeignKey to the auth user model:
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
EDIT
If I remove the user field from the Exercise model my view works, however I want to then be able to see details of each exercise, but have those details visible only to the user who created them. Is there another way that this can be achieved?
I see you have involved user in both of the models. This shouldn't be done.
You should user as Foreign Key in either of the Models not both. This would cause insolvency in your logic.
You can do it with a simple logic like : Remove user Foreign Key from Exercise
Models.py
class Exercise(models.Model):
name = models.CharField(max_length=100)
Views.py
from .forms import ExerciseForm as e_form
class ExerciseFormView(CreateView):
form_class = e_form
success_url = '/'
def form_valid(self, form):
form.save()
area = Area.objects.get(user__id=request.user.id)
new_ex = Exercise.objects.latest('id')
area.exercise.add(new_ex)
area.save()
form.instance.user = self.request.user
return JsonResponse({'exercise': model_to_dict(new_ex)}, status=200)
To get all exercises of a specific user
a = Area.objects.get(user__id=request.user.id)
exercises = a.exercise.all()
The solution was to create a user field with a foreign key to Profile in my Workout model and then instantiate the view.
Related
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.
In my Django app, I have defined a Many-to-one relationship using ForeignKey. Now what I want is that when a logged-in user submits the ListForm then his username should automatically add to the owner field of the ListModel. Currently when a user submits the form, None is being shown in the owner field, how can I add the username to the database along with the form?
my models.py:
from django.db import models
from django.contrib.auth.models import User
class ListModel(models.Model):
owner = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
task = models.CharField(max_length=255)
completed = models.BooleanField(default=False)
my forms.py:
from django.forms import ModelForm
from .models import ListModel
from django import forms
class ListForm(ModelForm):
class Meta:
model = ListModel
fields = ['owner','task', 'completed']
You have to override the form_valid() method of the View and attach the current logged in user as an Owner.
def form_valid(self, form):
form.instance.owner = self.request.user <------ This line will do the trick.
return super().form_valid(form)
Finally got it working (my solution reference)
First, we need to exclude the owner field from the ModelForm in forms.py:
class ListForm(ModelForm):
class Meta:
model = ListModel
fields = ['task', 'completed']
# instead of above line we can simply write: exclude = ['owner']
and in the views.py:
form = ListForm(request.POST)
if form.is_valid():
task_list = form.save(commit=False)
task_list.owner = request.user
task_list.save()
return redirect('/')
where instead of task_list we can use any variable & also note that after task_list.save() no need to do form.save() because it's already included in task_list = form.save(commit=False)
I'm trying to make some forms that will allow users to add some objects, delete them or edit but I've stucked with thing like author of model. Let's say we got model Shot which got field
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
Because I've created custom user model to expand user by some fields that I want, and then we creating modelForm, creating views etc. and finally got form. When we will try to submit this form, it won't add this object submited in form to db because form has no filled field author author which means this field == Null and that's why it won't add this to db. So my question is how to get it dynamic, for example when user with nick "thebestuser" will try to add this modelForm it will work and mark author as "thebestuser"? Ofc I could add to form field author, but it's the worst way in my opinion and every user would be allowed then to add object for example as a another user, let's say user with nick "anothernick" could add form as a user with "thebestuser" which is In my opinion not acceptable.
models.py
from django.db import models
from django.contrib.auth.models import User
from streamers.models import Streamer
from django.conf import settings
from django.utils import timezone
class Shot(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=70)
url = models.CharField(max_length=100)
streamer = models.ForeignKey(Streamer, on_delete=models.CASCADE)
published_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
forms.py
from django import forms
from .models import Shot
class AddShot(forms.ModelForm):
class Meta:
model = Shot
fields = [
'title',
'url',
'streamer',
]
views.py
#login_required
def add_shot(request):
form = AddShot(request.POST or None)
if form.is_valid():
form.save()
instance = form.save(commit=False)
instance.published_date = request.published_date
instance.author = request.user
instance.save()
context = {
'form': form
}
return render(request, 'shots/add_shot.html', context)
You'll need to do it in your view. When you save your form pass commit=False to your save method, add your user, then save the returned instance.
def my_view(request):
form = AddShot(request.POST)
instance = form.save(commit=False)
instance.author = request.user
instance.save()
Documented here: https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#the-save-method
I am trying to develop a small app a user can upload images after logging in. I have used ModelForm to generate the form for uploading the image and its decription. The ArtWork model has foreign key relationship with the User model. I exclude the user field from the ModelForm. Now I do not have any clue how to save the logged in user along with the form data into the database. Thanks in advance.
Here is my ArtWork Model
class Artwork(models.Model):
art_image = models.ImageField("Art Image", upload_to="images", blank=True,null=True)
art_name = models.CharField(max_length=100, blank=True,null=True)
user = models.ForeignKey(AuthUser, default=1)
def __unicode__(self):
return self.art_name
Here is my View
class UploadArtworkView(FormView):
form_class = ArtworkForm
success_url = "/"
template_name = "artwork/upload.html"
def form_valid(self, form):
#artwork = Artwork(art_image= self.get_form_kwargs().get('files')['art_image'])
#print "Art name is" + form.post['art_name']
form.save()
print self.request.user.name
return super(UploadArtworkView,self).form_valid(form)
Here is the Form
from django.forms import ModelForm
from .models import Artwork
class ArtworkForm(ModelForm):
class Meta:
model = Artwork
fields = ['art_image','art_name']
Your use case is very similar to this example in the Django docs. You have already realised that form_valid is the correct method to override, so you are very close to the answer. The trick is to access the object using self.form.instance, and set the user:
def form_valid(self, form):
form.instance.user = self.request.user
return super(UploadArtworkView,self).form_valid(form)
I have question regarding how to attach additional form to logged in users in Django.
I want that additional form belongs to logged in user and the data I enter in the form should goes under logged in user table. I am new to Django and python please have patience I hope i can explain correctly what i want to do with this
Data I shall enter for this view shall go under logged in user only basically i want to attach this view to the logged in user only Error I am getting is
Exception Value:
registration_todos.user_id may not be NULL
#models
class userProfile(models.Model):
user = models.OneToOneField(User)
birth =models.DateField()
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class todos(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=100)
created = models.DateField()
time = models.TimeField()
def __unicode__(self):
return unicode(self.user)
#forms additional form for todos
class formtodos(ModelForm):
title = forms.CharField(label=(u'Todo'))
created = forms.DateField(label=(u'Date'))
time = forms.TimeField(label=(u'Time'))
#user = forms.CharField(label=(u'username'))
class Meta:
model = todos
exclude=('user',)
#view
def modeltodo(request):
if request.user.is_authenticated():
todos.objects.filter(user=request.user)
if request.method == 'POST':
form =formtodos(request.POST)
if form.is_valid():# All validation rules pass
todoss = form.save(commit=False)
todoss.created_by = request.user
form.save()
return HttpResponseRedirect('/profile/')
else:
form = formtodos() # An unbound form
context = {'form':form}
return render_to_response('todo.html', context, context_instance=RequestContext(request))
you've specified exclude = ('user',) in your form. This means that when you try to save the form there is no user_id present which causes the error. You probably want to put this before the save() call: todoss.user = request.user