I am trying to male a django webapp; the app has several forms that are submitted by users and I was wondering if there was a way to tell which user submitted the form so that I could bind the form input to that particular user. The form is for an "appointment" as if the patient that were logged in is making an appointment to go see their doctor.
Model:
class Appointment(models.Model):
user = models.OneToOneField(User)
schedule = models.ForeignKey(Schedule)
doctorName = models.CharField(max_length=50)
date = models.DateTimeField(auto_now_add=True)
Form:
class CreateAppointment(forms.ModelForm):
class Meta:
model = Appointment
fields = ("doctorName", "date")
View:
def create_appointment(request):
if request.POST:
form = CreateAppointmentForm(request.POST, instance=request.user.profile)
if form.is_valid():
form.save()
return render_to_response('index.html', context_instance=RequestContext(request))
else:
form = CreateAppointmentForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('create_appointment.html', args, context_instance=RequestContext(request))
If the user is logged in then you can simply use this:
user=request.user
In your views.py. It will return AnonymousUser if the user is not logged in, so first make sure the user is authenticated.
if request.user.is_authenticated ():
#Do stuff
You are using instance improperly, it's for when you want to update a specific row in the database. You need to create the form without the user field (adding exclude=['user',] to the meta of the form f.ex.) then change the contents of the if request.method="POST" a bit:
form_obj = CreateAppointmentForm(request.POST).save(commit=False)
form_obj.user = request.user
form_obj.save()
Related
I'm building a page that allows users to edit Task and related Activity records (one task can have many activities), all on the same page. I want to allow the user to "adopt" one or more activities by ticking a box, and have their user record linked to each activity via a ForeignKey. Here are extracts from my code...
models.py
from django.contrib.auth.models import User
class Task(models.Model):
category = models.CharField(max_length=300)
description = models.CharField(max_length=300)
class Activity(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
notes = models.TextField(blank=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
The activity "owner" is linked to a User from the Django standard user model.
I added an extra field in the form definition for the adopt field - I don't want to add it to the model as I don't need to save it once it's done it's job.
forms.py
class ActivityForm(forms.ModelForm):
adopt = forms.BooleanField(required=False)
class Meta:
model = Activity
fields = '__all__'
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## ? DO SOMETHING HERE ? ##
formset.save()
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
I just can't figure out how to do that - if it was a single form (rather than an InlineFormSet), I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this). Or try save(commit = False) and update the model instance then save() it.
Maybe I have to iterate through the formset in the view code and try one of those options when I find one that had adopt=True?
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
formset = TaskInlineFormSet(request.POST, instance = task)
if formset.adopt:
# If True
formset.user = request.user
formset.save()
I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this).
You should give it a try.
I'm not happy with this solution but it does work. I iterate through the forms and change the object instance if my adopt field is set.
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## HERE'S WHAT I ADDED ##
for form in formset:
if form.cleaned_data['adopt'] is True:
form.instance.owner = request.user
## END OF ADDITIONS ##
formset.save()
## return redirect('manage_task',pk=task.id) # CHANGED THIS BECAUSE I WASN'T RETURNG ERRORS!
if not form.errors and not formset.total_error_count():
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
I wish I could find more in the docs about how the form saving works but I think I'll have to look into the code if I want more detail.
I want to send a single notification message to multiple users or all users. I have tried many to many fields but the sent to id won't save the i.d's of the users that i have sent the message to.
Models.py
class Notifications(models.Model):
id=models.AutoField(primary_key=True)
sent_to = models.ManyToManyField(CustomUser)
message = models.TextField(null=True)
message_reply = models.TextField(null=True)
created_at=models.DateTimeField(auto_now_add=True)
updated_at=models.DateTimeField(auto_now=True)
Views.py
def add_notification(request):
notifs = Notifications.objects.all()
users = CustomUser.objects.filter(is_staff=True)
if request.method == 'POST':
form = AddNotifForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.message_reply = "none"
instance.save()
sent_to = form.cleaned_data.get('sent_to')
messages.success(request, f'Message has been successfully sent .')
return redirect('add_notif')
else:
form = AddNotifForm()
context={
'notifs' : notifs,
'form' : form,
'users' : users,
}
template_name ='main-admin/add-notif.html'
return render(request, template_name, context)
Forms.py
class AddNotifForm(forms.ModelForm):
sent_to = forms.ModelMultipleChoiceField(
queryset=CustomUser.objects.filter(is_staff=True).exclude(is_superuser=True),
widget=forms.CheckboxSelectMultiple,
required=True)
class Meta:
model = Notifications
fields = ['sent_to', 'message']
From the docs:
every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass. After you’ve manually saved the instance produced by the form, you can invoke save_m2m() to save the many-to-many form data.
So you'll have to call save_m2m() after instance.save() if you are using commit=False:
instance = form.save(commit=False)
instance.message_reply = "none"
instance.save()
form.save_m2m() # <-- Add this
I have a website which catalogs local hikes. Users can "log" that they have completed these hikes. I have both of these models+forms working as intended. Right now, though, in order to log a hike, you have to select the hike from a long list which contains all the hikes in the database. I'd like to be able to pre-populate that field so that if you are coming from the detail page of the hike in question, then that field is filled in with the hike.
Here's some code:
models.py:
model Hike(models.Model):
name = CharField(max_length=255)
slug = models.SlugField(unique=True)
...other fields...
model UserLog(models.Model):
hike = models.ForeignKey(Hike, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
forms.py:
class LogHike(forms.ModelForm):
class Meta:
model = UserLog
fields = ('hike', 'date', ... other fields)
views.py:
def hike_detail(request, slug):
hike = Hike.objects.get(slug=slug)
log = UserLog.objects.filter(hike__slug=slug)
... more stuff here ...
return render(request, 'hikes/hike_detail.html' {
'hike': hike,
'log': log,
})
def log_hike(request):
if request.method == "POST":
form = LogHike(request.POST)
if form.is_valid():
obj = form.save(commit=False)
userid = request.user
obj.user = request.user
obj.save()
return redirect('user_profile', uid=userid.id)
else:
form = LogHike()
return render(request, 'log_hike.html', {'form': form})
So if a user is viewing the "hike_detail" view, I want to have a link that sends them to the "log_hike" view, but with the "Hike" field pre-populated based on the "Hike" that they came from. I think it might have something to do with the instance function? But I don't have a ton of experience with it. Is there an easy way to pass the data from the referring page in order to pre-populate the form?
You probably want to override your ModelForm __init__ method:
def __init__(self, *args, **kwargs):
super(LogHike, self).__init__(*args, **kwargs)
if 'hike' in kwargs:
self.fields['hike'].value = kwargs['hike']
Now all you need is another view which accepts a parameter passed and you're set. Extend your urls.py for that and then do something like:
def log_hike_with_pre_set_hike(request, *args, **kwargs):
if request.method == 'POST':
# see your code
else:
form = LogHike(hike=kwargs['hike'])
return render(request, 'log_hike.html', {'form': form})
Untested code, you might have to adapt it, I come from class-based views so it might be different for you.
You can pre-populate that form in log_hike when the request.method is get in the same way as when in post.
form = LogHike({'hike':hike_id})
The other thing is form where you'll take the hike_id. But that can come from request.GET for example.
I'm trying to password protect my registration page in Django without requiring the user to login, but I can't seem to figure it out. My flow should be:
User accesses mydomain.com/register/
User enters password into registration_access form
If unsuccessful, user re-enters password
If successful, user is presented with UserCreationForm
If UserCreationForm is not filled out properly, user is presented with UserCreationForm again + errors
If UserCreationForm is filled out properly, user is redirected to their profile page
The issue I'm having right now is that I can't redirect a user to a view without a URL (the view containing UserCreationForm).
Here's my code:
views.py
def register(request):
if request.method == 'POST':
# Gather information from all forms submitted
user_custom_info = user_information(request.POST)
user_info = UserCreationForm(request.POST)
profile_info = deejay_form(request.POST)
# Check to make sure they entered data into each of the forms
info_validated = user_info.is_valid() and user_custom_info.is_valid() and profile_info.is_valid()
# If they did...
if info_validated:
# Clean the data...
user_custom_info = user_custom_info.cleaned_data
user_info = user_info.cleaned_data
profile_info = profile_info.cleaned_data
# Create a new user with those traits
new_user = User.objects.create_user(user_info['username'], user_custom_info['email'], user_info['password1'])
new_user.first_name = user_custom_info['first_name']
new_user.last_name = user_custom_info['last_name']
new_user.save()
# Create a new deejay with those traits..
new_deejay = Deejay(user=new_user, dj=profile_info['dj'], role=profile_info['role'], bio=profile_info['bio'], phone=profile_info['phone'])
new_deejay.save()
# Log in the user..
if not request.user.is_authenticated():
this_user = authenticate(username=user_info['username'], password=user_info['password1'])
login(request, this_user)
# Need to add to group - http://stackoverflow.com/questions/6288661/adding-a-user-to-a-group-in-django
# Redirect to dj page
return redirect('dj_detail', dj_name=Deejay.objects.get(user=request.user).dj)
else:
return render(request, 'pages/backend/register.html', {'forms':[user_custom_info, user_info, profile_info]})
return render(request, 'pages/backend/register.html', {'forms':[user_information, UserCreationForm, deejay_form]})
# View for a password protected registration form
def register_protect(request):
if request.method == 'POST':
pw_info = registration_access(request.POST)
if pw_info.is_valid():
return redirect(register)
else:
return render(request, 'pages/backend/register.html', {'forms':[pw_info]})
return render(request, 'pages/backend/register.html', {'forms':[registration_access]})
forms.py
class user_information(forms.ModelForm):
first_name = forms.CharField(label='First Name', required=True)
last_name = forms.CharField(label='Last Name', required=True)
email = forms.EmailField(label='Email', required=True)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class deejay_form(forms.ModelForm):
class Meta:
model = Deejay
fields = ('dj', 'role', 'bio', 'phone')
class registration_access(forms.Form):
secret_password = forms.CharField(label="Secret Password", widget=forms.PasswordInput())
def clean(self):
access_password = "mypassword"
given_password = self.cleaned_data.get('secret_password')
if given_password != access_password:
raise forms.ValidationError("Did you forget your training?")
return self.cleaned_data
"Redirect" means, by definition, to redirect back to the server. Thus you need to redirect to a URL. You can redirect to the same URL, but then you'd need to write your view to be able to handle the different things you want to do.
To me, it sounds like you'd be better served using Javascript and handle things as a single page app.
I am trying to populate the field 'owner' in the my NoteForm. I read in documentation that I need to use the Admin for that.But i still get this error : note_note.owner_id may not be NULL. Need help. Code:
forms.py:
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title','body')
models.py:
class Note(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
cr_date = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, blank=False)
admin.py
class NoteAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj = form.save(commit=False)
obj.owner = request.user
obj.save()
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
instance.user = request.user
instance.save()
else:
fromset.save_m2m()
admin.site.register(Note, Noteadmin)
views.py:
def create(request):
if request.POST:
form = NoteForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponceRedirect('/notes/all')
else:
form = NoteForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('create_note.html', args)
I do not understand the point of Admin over here. From the code, what I understood is you creating a simple django form for your site and getting the error on form submission. If that's case, the solution is quiet easy. This error is generated because you are try to save a record in your Note model without any reference to User. As there's a db constraint on the foreign key field, it raises the error. Solution is easy, just add owner to the list of fields in the form or modify the save method to assign an owner to the note. If you'll use the first option, the user will be able to see and select the owner. And if you want to pre-populate that particular field, pass initial value to the form.