Referring to Users in Django models - django

I'm saving a list of values for each user on my Django app, taken from a daily poll. I can't get the 'User' field responder to save properly as it always opts for the default value 'Null'. Instead of a list of values for each user, I am getting a single list in the database for all the users combined.
Here is my model:
class Repondez(Model):
score_list = JSONField()
responder = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.score_list
Here is views.py:
r = Repondez()
r.score_list = []
global list_length
def home(request):
context = {}
return render(request, 'rating/home.html', context)
#login_required
def add(request):
if request.method == 'POST':
selected_option = (request.POST['rating'])
list_length = len(r.score_list)
if selected_option == 'option1':
r.score_list.append(1)
elif selected_option == 'option2':
r.score_list.append(2)
elif selected_option == 'option3':
r.score_list.append(3)
elif selected_option == 'option4':
r.score_list.append(4)
elif selected_option == 'option5':
r.score_list.append(5)
else:
return HttpResponse(400, 'Invalid form')
r.save()
return redirect(add)
context = {
'r' : r,
}
return render(request, 'rating/add.html', context)
I have tried to avoid having a default value for responder but am always asked for one during migrate. I tried a ForeignKey and ManytoManyField for the 'User' relationship as well but in each case I am getting a single, combined list for all users. How can I save the responder field properly so each list is saved for their respective user?

In your add view you need to set the responder field for the instance:
r.responder = request.user
r.save()
Ok to solve the list issue you are having I would change the model to use a foreign key to the user instead of onetoone field
class Respondez(models.Model):
class Meta:
ordering = (‘day’,)
responder = models.ForeignKey(
settings.AUTH_USER_MODEL
related_name=‘scores’)
score = models.IntegerField()
day = models.DateField(auto_add_now=True)
Then access the scores from the user instance like so:
request.user.scores.all()
You can also access the score list using values_list:
request.user.scores.all().values_list(‘score’, flat=True)

Related

Object of type Cart is not JSON serializable

I have a products model and a cart model. There is a ManyToMany relation that I put in cart model.
I am trying to return the object of cart model along with the context dictionary but it is giving this error "Object of type Cart is not JSON serializable".
I am using Django version '3.0.7'.
I am following Justing Michael's Tutorial,He is using lower Django version and its working on that.
Did Django change this thing?
class CartManager(models.Manager):
def new_or_get(self,request):
cart_id = request.session.get('cart_id')
qs = self.get_queryset().filter(id = cart_id)
if qs.count() == 1:
new_obj = False
cart_obj = qs.first()
if request.user.is_authenticated and cart_obj.user is None:
cart_obj.user = request.user
cart_obj.save()
else:
cart_obj = Cart.objects.new_cart(user = request.user)
new_obj = True
request.session['cart_id'] = cart_obj
return cart_obj,new_obj
def new_cart(self,user=None):
user_obj = None
if user is not None:
if user.is_authenticated:
user_obj = user
return self.model.objects.create(user=user_obj)
and the model actually is:
class Cart(models.Model):
user = models.ForeignKey(User,on_delete = models.CASCADE,null=True,blank=True)
products = models.ManyToManyField(Product,blank=True)
#more fields
and views.py is:
def cart_home(request):
cart_obj,new_obj = Cart.objects.new_or_get(request)
context={'cart':cart_obj}
return render(request,'carts/cart_home.html',context)
You can't send a Python class instance (such as a Model class instance) as a render data, you must send a json like object (a dict in Python).
So what you need is to convert cart_obj to a dict so it can be sent to the frontend through a render. The easiest way to do it with Django is to use a serializer (In your case, the best option is a ModelSerializer).
def cart_home(request):
cart_obj,new_obj = Cart.objects.new_or_get(request)
cart_obj_dict = CartSerializer(cart_obj).data
context={'cart':cart_obj_dict}
return render(request,'carts/cart_home.html',context)
I am sorry to disturb you guys,I had made an idiotic mistake:
request.session['cart_id'] = cart_obj
I was trying to assign cart object to session variable which was a typo ,instead in reality I should have done this:
request.session['cart_id'] = cart_obj.id
Credits: minglyu

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', {})

Use a different form based on variable

I have a "product" field that I want to use to determine which form to display. I am trying to do this in the view but wondering if I should do it in the template instead. I have tried the following but "form" does not get assigned by my if statements. What am I doing wrong?
#login_required
def update_message(request, pk):
message = get_object_or_404(Submission, pk=pk)
author = message.author
date_posted = message.date_posted
product = message.product
message_obj = Submission.objects.get(pk=pk)
program_type = message.program_type
if author == request.user:
if request.method == 'POST':
if product == 'Apple':
form = AppleForm(request.user, request.POST, instance=message)
if product == 'Orange':
form = OrangeForm(request.user, request.POST, instance=message)
if form.is_valid():
message_sub = form.save(commit=False)
message_sub.author = request.user
message_sub.date_posted = timezone.now()
message_sub.save()
form.save_m2m()
messages.success(request, 'Message updated')
return redirect('submission-list')
else:
if product == 'Apple':
form = AppleForm(request.user, instance=message)
if product == 'Orange':
form = OrangeForm(request.user, instance=message)
else:
messages.warning(request, 'You can't do that.')
return redirect('message-submission-list')
return render(request, 'programs/submission_create_form.html', {'product':product,'form': form, 'message_obj': message_obj,'program_type':program_type})
class MessageSubmission(models.Model):
message = models.CharField(max_length=5000)
author = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateTimeField(default=timezone.now)
program_code = models.ManyToManyField(Program)
program_type = models.CharField(max_length=30, blank=True)
product = models.ForeignKey('Product', on_delete=models.SET_NULL, null=True)
production_cycle = models.ManyToManyField('ProductionCycle', null=True)
def get_absolute_url(self):
return reverse('submission-list')
def __str__(self):
return self.message
As I mentioned in the comment, the issue is that product is a ForeignKey to another model. In the template, the FK will display using the __str__ method of that model, but that doesn't make it equal to that display value. You should compare explicitly with the relevant field on the target model:
if product.fruit_type == 'Orange' # or whatever the field is
(Alternatively you could do if str(product) == 'Orange' but that's more brittle and is coupling display logic in a way that's not very nice.)
There's nothing wrong with doing this in the views. If the form is not defined after those if statements then it means that the value of product is not Apple or Orange, but something else. I would double check the value of product to fix the issue.
Since Product is a class, you should reference a field. You didn't post the code for it, but for example
if form == product.name
If there is a name field.

None Type Error Attribute

I've been trying to implement a shopping cart on my website based on Django.
Below are the models I used:
class ShoppingCart(models.Model):
songs = models.ManyToManyField(Song)
albums = models.ManyToManyField(Album)
class Listener(User):
name = models.CharField(max_length=200, blank=True, null=True)
bio = models.TextField(blank=True, null=True)
cart = models.ForeignKey(ShoppingCart, blank=True, null=True)
Here is the views.py where I get an error saying None Type Object has no attribute songs at request.user.listener.cart.songs.add():
def add_to_cart(request):
if not request.user.is_authenticated() or not request.method == 'POST':
return HttpResponse(json.dumps({'success':False}), content_type='application/json')
typeobj = request.POST.get('type', None)
obj = request.POST.get('id', None)
if typeobj is None or obj is None:
return HttpResponse(json.dumps({'success':False}), content_type='application/json')
if typeobj == 'album':
try:
album = Album.objects.get(pk=obj)
except ObjectDoesNotExist:
return HttpResponse(json.dumps({'success':False}), content_type='application/json')
request.user.listener.cart.albums.add(Album.objects.get(pk=obj))
else:
try:
song = Song.objects.get(pk=obj)
except ObjectDoesNotExist:
return HttpResponse(json.dumps({'success':False}), content_type='application/json')
request.user.listener.cart.songs.add(Song.objects.get(pk=obj))
return HttpResponse(json.dumps({'success':True}), content_type='application/json')
I checked in the shell and the same error occurs when I try to add a song to the cart. It says cart is a NoneType object and has no attribute songs.
Thanks in advance.
I think you should use OneToOne relationship between User and ShoppingCart.
Sample model:
class Listener(User):
name = models.CharField(max_length=200, blank=True, null=True)
bio = models.TextField(blank=True, null=True)
cart = models.OneToOneField(ShoppingCart, blank=True, null=True)
In your view create a cart for user if it does not exists
As
def add_to_cart(request):
if not request.user.is_authenticated() or not request.method == 'POST':
return HttpResponse(json.dumps({'success':False}), content_type='application/json')
typeobj = request.POST.get('type', None)
obj = request.POST.get('id', None)
if typeobj is None or obj is None:
return HttpResponse(json.dumps({'success':False}), content_type='application/json')
if request.usr.listener.cart == None:
cart = ShoppingCart()
cart.save()
request.usr.listener.cart = cart
request.usr.listener.save()
# your code to add items in cart
request.user is an instance of the User object of the currently logged in user.
There is no relation between your Listener model and the User model (even though you inherited from it), so whatever you are trying to do, it won't work. In fact, even if there was a relationship, you'd be seeing these errors because you are not using the object relations correctly.
If you want to track a shopping cart for each user permanently, you need to add a ForeignKey to your ShoppingCart model to point to the User model.
If you just want to track the cart for a session; then use sessions to do so.
It might benefit you from going through the relations section of the documentation, since you aren't using add() correctly either.

Why is self.instance not set in clean function for a bound form?

I have a Django 1.1 model with unique_together on the owner and title where owner is foreign key on a user. This constraint is enforced but only after the clean. According to Django docs, I should be able to access self.instance to see non-form field object properties of a model instance.
However, I get the error
'JournalForm' object has no attribute 'instance'
Why is self.instance not set on this bound form in either the form clean() or the field clean_title() methods?
My model:
class Journal (models.Model):
owner = models.ForeignKey(User, null=True, related_name='journals')
title = models.CharField(null=False, max_length=256)
published = models.BooleanField(default=False)
class Meta:
unique_together = ("owner", "title")
def __unicode__(self):
return self.title
My form:
class JournalForm (forms.Form):
title = forms.CharField(max_length=256,
label=u'Title:')
html_input = forms.CharField(widget=TinyMCE(attrs={'cols':'85', 'rows':'40'}, ),
label=u'Journal Content:')
published = forms.BooleanField(required=False)
def clean(self):
super(JournalForm, self).clean()
instance = self.instance
return self.cleaned_input
def clean_title(self):
title = self.cleaned_data['title']
if self.is_bound:
if models.Journal.objects.filter(owner.id=self.instance.owner.id, title=title).exclude(id=self.instance.id).count() > 0:
raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.')
else:
if models.LabJournal.objects.filter(owner.id=self.instance.owner.id, title=title).count() > 0:
raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.')
return title
As requested - the view code:
def journal (request):
try:
journal = models.Journal.objects.get(id=id)
if request.method == 'GET':
if request.user.is_active:
if request.user.id == journal.owner.id:
data = {
'title' : journal.title,
'html_input' : _journal_fields_to_HTML(journal.id),
'published' : journal.published
}
form = forms.JournalForm(initial=data)
return shortcuts.render_to_response('journal/Journal.html', { 'form':form, })
else:
return http.HttpResponseForbidden('<h1>Access denied</h1>')
else:
return _display_login_form(request)
elif request.method == 'POST':
if LOGIN_FORM_KEY in request.POST:
return _handle_login(request)
elif request.user.is_active and request.user.id == journal.owner.id:
form = forms.JournalForm(data=request.POST)
if form.is_valid():
journal.title = form.cleaned_data['title']
journal.published = form.cleaned_data['title'];
journal.save()
if _HTML_to_journal_fields(journal, form.cleaned_data['html_input']):
html_memo = "Save successful."
else:
html_memo = "Unable to save Journal."
return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'saved':html_memo})
else:
return shortcuts.render_to_response('journal/Journal.html', { 'form':form })
return http.HttpResponseNotAllowed(['GET', 'POST'])
except models.Journal.DoesNotExist:
return http.HttpResponseNotFound('<h1>Requested journal not found</h1>')
Well there are a couple of issues here.
First is that you're not using a ModelForm. The docs you link to are for those, not for standard forms.
Secondly, in order for the form to have an instance attribute, you need to pass that instance in when you're instantiating the form.
If you do use a ModelForm, you won't need the code that converts the journal fields to the form fields, and vice versa on save - the form does that for you. You'll also be able to remove the clean_title method which checks for uniqueness, because that's already defined by the unique_together constraint on the model, and the ModelForm will enforce that for you.