Django Initial for a ManyToMany Field - django

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))

Related

Django: Foreign Key to User -> Form is not validating because field is required

I'm currently creating a Registration-Page with two parts
One part is about the Username and a Passwort.
The second part is about choosing the own PC-Configuration
After defining everything, the User can register to get to the Main-Page.
Therefore I got a Model called "PC_Configuration" with a bunch of Foreign-Keys to the different Database-Models of the Processors/Graphicscards etc.:
class PC_Configuration(models.Model):
user = models.ForeignKey(User, related_name='user_id', on_delete=models.DO_NOTHING)
processor = models.ForeignKey(Processors, related_name='processor_id', on_delete=models.DO_NOTHING)
graphicscard = models.ForeignKey(Graphicscard, related_name='graphicscard_id', on_delete=models.DO_NOTHING)
os = models.ForeignKey(OS, related_name='os_id', on_delete=models.DO_NOTHING)
ram = models.ForeignKey(RAM, related_name='ram_id', on_delete=models.DO_NOTHING)
harddrive = models.ForeignKey(Harddrive, related_name='harddrive_id', on_delete=models.DO_NOTHING)
Also, there is one ForeignKey to the User to connect the Configuration to the respective User-ID.
Inside views.py, I've been creating a DropdownForm for all the Dropdown-Fields which the User shall choose on his own:
class DropdownForm(forms.ModelForm):
class Meta:
model = models.PC_Configuration
exclude = []
def __init__(self, *args, **kwargs):
super(DropdownForm, self).__init__(*args, **kwargs)
self.fields['processors'].queryset = DropdownForm.objects.all()
self.fields['processors'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['graphicscard'].queryset = DropdownForm.objects.all()
self.fields['graphicscard'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['os'].queryset = DropdownForm.objects.all()
self.fields['os'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['ram'].queryset = DropdownForm.objects.all()
self.fields['ram'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['harddrive'].queryset = DropdownForm.objects.all()
self.fields['harddrive'].label_from_instance = lambda obj: "%s" % obj.name
But regarding the fact, that the User-ID shall be assigned to the Configuration automatically, there's no field for that in here.
It is defined in the register_view(request) - Method:
def register_view(request):
form = DropdownForm()
if request.method == "POST":
form = DropdownForm(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
myuser = User.objects.create_user(username, None, password)
myuser.save()
auth.login(request, myuser)
#form.user = request.user
print(form.errors)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
messages.success(request, "Account has been created successfully")
return redirect(reverse('gamesearch_view'))
else:
print('Failed')
form = DropdownForm()
render(request, 'register.html', dict(form=form))
return render(request, 'register.html', dict(form=form))
And in here, we got the problem, I guess.
While Testing the Registration, the Testaccounts keep creating and login successfully. But the problem is, that there's no PC-Configuration created because the form is not validating.
With
print(form.errors)
I've been trying to figure out why exactly and it said
<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
So it seems like it's necessary to define the "user"-field before checking, if the form is validating and defining the user inside an instance afterwards.
That's why I was trying to do this:
form.user = request.user
But it's still not working and I can't figure out, what's exactly the problem since "user" shouldn't be part of the form-validation.
Can you help me out here?
Thank you in Advance!
You'll have a simpler time with something like this...
Your related_names were somewhat bogus; they're supposed to be the reverse name from the "viewpoint" of the other model. (Also, you never need to add _id to your fields by hand in Django.) If you elide the related_names, they'll implicitly be pc_configuration_set.
on_delete=DO_NOTHING is likely not a good idea. PROTECT is a good default.
It's easier to just handle the username and password as fields in the form.
You were missing exclude = ["user"], so if your template didn't render a field for user, of course it'd be missing. However, you also don't want the POSTer of the form to submit any old user id.
Using a FormView removes most of the boilerplate required to manage forms.
We're using transaction.atomic() to make sure the user doesn't get finally saved to the database if saving the PC Configuration fails.
We assign the created user to form.instance, which is the new but as-of-yet unsaved PC Configuration.
(Of course, imagine these are in separate files.)
from django import forms
from django.db import models, transaction
from django.views.generic import FormView
class PC_Configuration(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
processor = models.ForeignKey(Processors, on_delete=models.PROTECT)
graphicscard = models.ForeignKey(Graphicscard, on_delete=models.PROTECT)
os = models.ForeignKey(OS, on_delete=models.PROTECT)
ram = models.ForeignKey(RAM, on_delete=models.PROTECT)
harddrive = models.ForeignKey(Harddrive, on_delete=models.PROTECT)
class RegisterAndConfigurePCForm(forms.ModelForm):
username = forms.CharField(required=True)
password = forms.CharField(required=True, widget=forms.PasswordInput())
class Meta:
model = PC_Configuration
exclude = ["user"] # we'll assign this by hand
class RegisterAndConfigureView(FormView):
form_class = RegisterAndConfigurePCForm
template_name = "register.html"
def form_valid(self, form):
with transaction.atomic():
user = User.objects.create_user(form.cleaned_data["username"], None, form.cleaned_data["password"])
form.instance.user = user # assign user to the to-be-created PC configuration
form.save()
return redirect(reverse("gamesearch_view"))

With Django, how do I reference an existing model in a form save method instead of creating a new instance?

I'm trying to use a ModelChoiceField to display options populated from model, and when a user selects a choice, store that method in a different model.
I'm using a standard form instead of a ModelForm, because I wasn't able to get the form to display how I wanted to when using a Modelform.
My issue is that in my form save method, a new instance is created, which is not what I want.
Here are the relevant models:
class Client(models.Model):
client_email = models.EmailField(max_length = 254)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
phone = PhoneField(blank=True)
assigned_manager = models.ForeignKey(Manager, on_delete=models.CASCADE, blank=True, null=True)
created_date = models.DateTimeField(default=timezone.now)
#property
def full_name(self):
return '{0} {1}'.format(self.first_name, self.last_name)
class Manager(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
manager_email = models.EmailField(max_length = 254)
username = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
#property
def full_name(self):
return '{0} {1}'.format(self.first_name, self.last_name)
My view:
def manageclient(request, urlid):
client = Client.objects.get(id=urlid)
form = AssignManagerForm()
if request.method == "POST":
form = AssignManagerForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'mysite/manageclient.html', {})
else:
form = AssignManagerForm()
context = {
'client': client,
'urlid': urlid,
'form': form,
}
return render(request, 'mysite/manageclient.html', context)
And my forms.py
class AssignManagerForm(forms.Form):
full_name = forms.ModelChoiceField(queryset=Manager.objects.all())
def save(self):
data = self.cleaned_data
client = Client(assigned_manager=data['full_name'])
client.save()
What I need to do is pass the urlid in my view to my save method in my forms.py, but I am unsure how to do that. Even if i could do that, I'm not sure how to modify form save to use urlid to refer to a specific record and set only the assigned_manager record.
Additionally, while I want the meta field to be used to display the form, I know it isn't what should be being passed to the assigned_manager field. How would I pass a Manager of instance to establish the foreign key relationship?
edit: edited to correct queryset in forms.py as per comments
Here is a solution using a ModelForm, by using a ModelForm you no longer have to manually set attributes on save or provide initial values when updating an existing instance.
The field assigned_manager will still be named assigned_manager but it's label can be overridden to be whatever you want it to be by passing labels in the ModelForm.Meta
class AssignManagerForm(forms.ModelForm):
class Meta:
model = Client
fields = ['assigned_manager']
labels = {'assigned_manager': 'Full name'}
def manageclient(request, urlid):
client = Client.objects.get(id=urlid)
if request.method == "POST":
form = AssignManagerForm(request.POST, instance=client)
if form.is_valid():
client = form.save()
# The general convention is to redirect after a successful POST
else:
form = AssignManagerForm(instance=client)
context = {
'client': client,
'urlid': urlid,
'form': form,
}
return render(request, 'mysite/manageclient.html', context)
Instead of saving it in form, you can directly do this operation in view. For example:
def manageclient(request, urlid):
client = Client.objects.get(id=urlid)
if request.method == "POST":
form = AssignManagerForm(request.POST)
if form.is_valid():
client.assigned_manager = form.cleaned_data['full_name']
client.save()
return render(request, 'mysite/manageclient.html', {})

Django - display and saving in a simple form

This is a very beginner-orientated question but I've been stuck on this issue all day.
I would like to load the data for a specific record and be able to save it (Submit button in a template) but i'm still trying to understand instances and the save method.
models.py
class model_essays(models.Model):
user = models.ForeignKey(User, default='1', on_delete=models.CASCADE)
title = models.CharField(max_length=100, default='')
date_added = models.models.DateTimeField(auto_now_add=True)
body = models.TextField()
def __str__(self):
return self.title
I understand the id is created automatically
forms.py
class frm_essays (forms.ModelForm):
class Meta:
model = model_essays
fields = ['title', 'date_added', 'body']
urls.py
urlpatterns = [
path('essay/<int:pk>', views.views_essay),
]
views.py {stuck here}
#login_required
def views_essay(request, pk):
if request.method == 'POST':
updatedForm = essay_detail(request.POST, instance=request.? {I want the ID of the essay})
if u_form.is_valid():
updatedForm.save()
messages.success(request, f'this essay has been updated')
return redirect('essay_detail')
else:
updatedForm = frm_essays(instance=request.{as above})
context = {
'updatedForm': updatedForm
}
return render(request, 'essay_detail.html', context)
On the decorator - I haven't gotten around to only allowing the users to view their own created essays, this would be the next large hurdle but not the issue I'm asking about.
Unless I am mistaken you are looking for the same ID as the pk (primary key). You have that passed in as an argument to your function.
You just need to query the instance from the DB.
def views_essay(request, pk):
essay = model_essays.objects.get(pk=pk)
if request.method == 'POST':
updatedForm = essay_detail(request.POST, instance=essay)
...
Note: essay will be None if the query based on the pk does not find an match in the DB.

Setting form fields in django class based generic view CreateView

I'm using django's CreateView to add images to a book. I pass the book's id to the class based view as a parameter in the url. Form fields such as book and language are not rendered on the template, rather they're obtained with the help of the book's id.
# views.py
class PictureCreateView(CreateView):
model = Upload
fields = "__all__"
book_id = None
def get_initial(self):
initial = super(PictureCreateView, self).get_initial()
initial = initial.copy()
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
initial['book'] = book
initial['language'] = language
initial['uploader'] = self.request.user
return initial
# set book_id so it used in the template
def get_context_data(self, **kwargs):
context = super(PictureCreateView, self).get_context_data(**kwargs)
context['book_id'] = self.book_id
return context
def form_valid(self, form, **kwargs):
print('Form is valid')
self.object = form.save()
files = [serialize(self.object)]
data = {'files': files}
response = JSONResponse(data, mimetype=response_mimetype(self.request))
response['Content-Disposition'] = 'inline; filename=files.json'
return super(PictureCreateView, self).form_valid(form)
def form_invalid(self, form):
print('Form invalid!')
print(form.errors)
data = json.dumps(form.errors)
return HttpResponse(content=data, status=400, content_type='application/json')
# models.py
class Upload(models.Model):
image = models.ImageField(upload_to=get_upload_path, help_text='Image to process')
uploader = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name='uploader')
language = models.ForeignKey(Language, models.CASCADE)
book = models.ForeignKey(Book, models.CASCADE)
The problem is that I get an error saying the form is invalid, and the fields uploader, book and language are required. How do I resolve this?
The initial data is used to display the defaults when the form is initially displayed. It isn't used when those values are missing from the submitted form data. If fields like book and uploader are set from the URL or logged-in user, then you should leave them out of the form completely, instead of setting them in the initial data. You can then set the values on the instance in the form_valid method before the form is saved.
from django.contrib.auth.mixins import LoginRequiredMixin
class PictureCreateView(LoginRequiredMixin, CreateView):
model = Upload
fields = ['other_field1', 'other_field2', ...] # leave out book, language and uploader
def form_valid(self, form):
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
form.instance.book = book
form.instance.language = ????
form.instance.uploader = self.request.user
return super(
The LoginRequiredMixin makes sure that only logged-in users can access the view.
You may want to use get_object_or_404 to handle the case where book_id refers to a book that does not exist.
One thought, initial doesn't fill the model for submission. You need to do that in init
def __init__(self):
super(PictureCreateView, self).__init__()
self.fields['book'] = self.initial['book']
self.fields['uploader'] = self.initial['uploader']
self.fields['language'] = self.initial['book']
Or, if you don't want to set the fields, make sure they are optional in your original model:
class Upload(models.Model):
uploader = models.ForeignKey('uploader', on_delete=models.CASCADE, null=True, blank=True)
book = models.ForeignKey('book', on_delete=models.CASCADE, null=True, blank=True)
language = models.ForeignKey('language', on_delete=models.CASCADE, null=True, blank=True)

I need advice to integrate an app to my project

I created an app to audit operations on objects. When an object is created, updated or deleted, a new record is created in my audit table (Auditor model).
The Audit app is working now, but to use it on my models requires some work and a lot of code that I believe can be avoided with a most optimized approach.
Which django resources or approach can I use to integrate the Audit app on my models, without write so much code? I need a simplified way to archieve this integration of the Auditor app with all my models and other projects.
I'll give an example how I using the Auditor model with a model (Car) that I want to audit.
Thank you.
Car model
class Car(models.Model):
name = models.CharField(max_length=50)
brand = models.CharField(max_length=50)
color = models.CharField(max_length=50)
is_available = models.BooleanField(default=True)
Auditor model
class Auditor(models.Model):
field = models.CharField(max_length=255, blank=True)
action = models.CharField(max_length=6)
old_value = models.TextField(blank=True)
new_value = models.TextField(blank=True)
stamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
content_type = models.ForeignKey(ContentType, blank=True)
object_id = models.PositiveIntegerField(blank=True)
content_object = GenericForeignKey('content_type', 'object_id')
deleted_object = models.CharField(max_length=255, blank=True)
Car view
from audittest.apps.auditor.models import Auditor
from django.contrib.contenttypes.models import ContentType
#Function for audit creation. I know that the view is not the right place to put this function, but I put this here for test.
def create_audit(obj, request, action, obj_id=False):
if action == 'CREATE':
audit = Auditor(content_type = ContentType.objects.get_for_model(obj), object_id = obj.id, user = request.user, action = action)
elif action == 'DELETE':
audit = Auditor(content_type = ContentType.objects.get_for_model(obj), object_id = obj_id, user = request.user, action = action, deleted_object = obj)
audit.save()
def new(request, template_name='cars/form.html'):
form = CarForm(request.POST or None)
if form.is_valid():
obj = form.save()
create_audit(obj, request, 'CREATE')
return redirect('car:admin')
return render(request, template_name, {'form':form, 'title':u'Novo Car'})
def edit(request, pk, template_name='cars/form.html'):
car = get_object_or_404(Car, pk=pk)
form = CarForm(request.POST or None, instance=car, request=request)
if form.is_valid():
form.save()
return redirect('car:admin')
return render(request, template_name, {'form':form,'title':u'Editar Car'})
def delete(request, pk, template_name='cars/confirm_delete.html'):
car = get_object_or_404(Car, pk=pk)
obj_id = car.id
if request.method=='POST':
car.delete()
create_audit(car, request, 'DELETE', obj_id=obj_id)
messages.success(request, u'Car excluĂ­do com sucesso.')
return redirect('car:admin')
return render(request, template_name, {'object':car})
Car form
class CarForm(ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(CarForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(CarForm, self).clean()
# Audit updated fields
if self.instance.pk is not None:
fields = []
for field in self.instance._meta.get_all_field_names():
if field != 'id' and getattr(self.instance, field) != cleaned_data[field]:
#fields.append((field, getattr(self.instance, field), cleaned_data[field]))
audit = Auditor(content_type = ContentType.objects.get_for_model(self.instance), object_id = self.instance.pk, user = self.request.user, action = 'UPDATE', field = self.instance._meta.get_field(field).verbose_name, old_value = getattr(self.instance, field), new_value = cleaned_data[field])
audit.save()
return cleaned_data
Use class based views in your applications so you can use the full advantages of the cool Mixins,
I usually create a Mixin that can be added to any CreateView or UpdateView.
class withAudit(object):
"""
A mixin that will create an audit record wither the action is
Create or Update
"""
def get_success_url(self):
"""
This will be called when the form is valid and saved.
"""
# create the record
audit = Auditor(content_type= ContentType.objects.get_for_model(self.model))
audit.object_id = self.object.pk
audit.user = request.user
# You will need a way to capture this action.
# there are many ways to do it.
audit.action = "Create"
audit.save()
return super(withAudit, self).get_success_url()
In your views you have to use it this way
class CarCreate(withAudit, CreateView):
model = Car
For update
class CarUpdate(withAudit, UpdateView):
model = Car
You can do the following to any UpdateView or CreateView in your application. However, For Deleting the object, I think you will need another mixin which will capture the data before performing the action. You need to see the class based views docs in order to customise these as you want.
The same idea can be done with decorators if you really want keep using method based views.
If you have a big application with high traffic, this process should be done in the background where you define a stack or queue an you keep passing these information to it, which will provide a better performance indeed. Some big applications using another database for logs and audit.