django i can't save form with 2 foreign keys - database

i have a Reply class:
class Reply(models.Model):
reply_to = models.ForeignKey(New)
creator = models.ForeignKey(User)
reply = models.CharField(max_length=140,blank=False)
a replay form:
class ReplyForm(ModelForm):
class Meta:
model = Reply
fields = ['reply']
where New is the Post class (containing users posts)
and a view
def save_reply(request):
#u = New.objects.get(pk=id)
if request.method == 'POST':
form = ReplyForm(request.POST)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.creator = request.user
new_obj.reply_to = form.reply_to
# reply_to_id = u
new_post = New(2) #this works hardcoded, but how can i get the blog New post #id, as a parameter, instead?
new_obj.reply_to = new_post
new_obj.save()
return HttpResponseRedirect('.')
else:
form = ReplyForm()
return render_to_response('replies/replies.html', {
'form': form,
},
context_instance=RequestContext(request))
where created_by belongs to New class and represents the creator of the post (which is to be replied)
how can i assign the current post to the reply under it?
thanks in advance!

I may have missed something, but reply_to needs an instance of the New model. New.id doesn't look like one to me?
new_obj.reply_to = New.id
Do you have an instance of the New model available at that point that you can assign?
ah, I see you've tweaked the question
If you don't have an instance of the New model, you'll need to create one
new_post = New(whatever, goes, here)
new_post.save()
Then assign it to reply_to
new_obj.reply_to = new_post
Or similar.
edit
Without knowing exactly that ReplyForm looks like I'm guessing a bit, but presumably it's based on the Reply object, letting the user select the reply_to field somehow or other?
Assuming that the form's reply_to variable is populated & correct I think you should just be able to do:
form = ReplyForm(request.POST)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.creator = request.user
new_obj.reply_to = form.reply_to
new_obj.save()
In fact since it's a foreign key, the new_obj = form.save(commit=False) may have already set .reply_to for you? The Django Model Forms docs may help.

Not sure if this'll be appropriate for your app or not, but you could try making use of a form widget, in particular the HiddenInput one to include the post (id) in the form. Something like
class ReplyForm(ModelForm):
reply_to = forms.ModelChoiceField(New.objects.all(), widget=forms.HiddenField)
class Meta:
model = Reply
fields = ['reply', 'reply_to']
widgets = {
'reply_to': HiddenField,
}
(Not sure that's entirely correct but see overriding-the-default-field-types-or-widgets for more).
You've now enabled the id you need to be passed to the client and back through the form, you now just need to put it in when you create the form for display in the first place
else:
form = ReplyForm()
form.reply_to = # ... fill in the current post (New inst) being replied to
# presumably from somewhere in the request object?
return render_to_response('replies/replies.html', { 'form': form, },
Hopefully that doesn't lead you off on the wrong track - completely untested, E&OE, YMMV, etc, etc

Related

Got AttributeError when attempting to get a value for field `people` on serializer `commentsSerializer`

I am building a blog website and I am using Django rest framework
I want to fetch top 2 comments for a particular post along with their related data such as user details.
Now I have user details in two models
User
People
and the comments model is related to the user model using foreign key relationship
Models ->
Comments
class Comment(models.Model):
comment = models.TextField(null=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE,
related_name='comments_post')
user = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='comments_user')
The People model is also connected to the user model with a foreign key relationship
People Model ->
class People(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,related_name='people')
Name = models.CharField(max_length=255,null=True)
following = models.ManyToManyField(to=User, related_name='following', blank=True)
photo = models.ImageField(upload_to='profile_pics', blank=True,null=True)
Phone_number = models.CharField(max_length=255,null=True,blank=True)
Birth_Date = models.DateField(null=True,blank=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
for fetching the comments I am using rest-framework and the serializers look like this
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=255)
class peopleSerializer(serializers.Serializer):
Name = serializers.CharField(max_length=255)
class commentsSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
comment = serializers.CharField(max_length=255)
Created_date = serializers.DateTimeField()
user = UserSerializer()
people = peopleSerializer()
The query to fetch the comments look like this ->
post_id = request.GET.get('post_id')
comments = Comment.objects.filter(post_id=post_id).select_related('user').prefetch_related('user__people').order_by('-Created_date')[:2]
serializer = commentsSerializer(comments, many=True)
return Response(serializer.data)
I am getting this error ->
Got AttributeError when attempting to get a value for field `people` on serializer `commentsSerializer`. The serializer field might be named incorrectly and not match any attribute or key on the `Comment` instance. Original exception text was: 'Comment' object has no attribute 'people'.
Unable to find a way out.
The source is user.people, not people, so:
class commentsSerializer(serializers.Serializer):
# …
people = peopleSerializer(source='user.people')
In the .select_related(…) [Django-doc] to can specify user__people: this will imply selecting user and will fetch the data in the same query, not in an extra query as is the case for .prefetch_related(…) [Django-doc]:
post_id = request.GET.get('post_id')
comments = Comment.objects.filter(
post_id=post_id
).select_related('user__people').order_by('-Created_date')[:2]
serializer = commentsSerializer(comments, many=True)
return Response(serializer.data)
Note: normally a Django model is given a singular name, so Person instead of People.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: created_date instead of Created_date.

Django: Updating models, new instance created instead of updated

i am trying to make a simple profile edit form for users on a website. I've followed the standard advice for updating, in the docs it says that Django detects the instances primary key and knows to update instead of insert.
only problem is, i get an insert when i am trying to update. I pre populate a form with a model instance (the instance that im trying to edit) but when i try and save it, i get a new instance. When i add the 'force_update=True' line, i get an error message that tells me that no primary key is detected. Not sure why, because im pre populating the form with a model instance, although, obviously the pk is not a part of the form. is there something im missing?
some code:
the model:
class profile(models.Model):
user = models.ForeignKey(User)
first_name = models.CharField(max_length=20, null=True)
last_name = models.CharField(max_length=20, null=True)
DOB = models.DateField(null=True)
age = models.IntegerField(null=True)
public_email = models.EmailField(null=True)
county = models.CharField(max_length=20, null=True)
town = models.CharField(max_length=30, null=True)
the form:
class profileForm(forms.ModelForm):
class Meta:
model = profile
exclude = ['user']
the view:
#login_required()
def edit_profile(request):
if request.POST:
proform = profileForm(request.POST)
if proform.is_valid():
prof = proform.save(False)
prof.user = request.user
prof.save(force_update=True)
return HttpResponseRedirect('/accounts/view_profile/')
else:
c = {}
if profile.objects.filter(user=request.user).exists():
prof = profile.objects.get(user=request.user)
c['proform'] = profileForm(instance=prof)
else:
c['proform'] = profileForm()
return render(request, 'edit_profile.html', c)
any help greatly appreciated!
i got it, turns out i was trying to just calling save() on the form without specifying the particular instance that the form relates to.
code:
#login_required()
def edit_profile(request):
c = {}
if profile.objects.filter(user=request.user).exists():
profModel = profile.objects.get(user=request.user)
c['proform'] = profileForm(instance=profModel)
else:
c['proform'] = profileForm()
if request.POST:
# this line here, added 'instance=profModel' to specify
# the actual instance i want to save
proform = profileForm(request.POST, instance=profModel)
if proform.is_valid():
prof = proform.save(False)
prof.user = request.user
prof.save()
return HttpResponseRedirect('/accounts/view_profile/')
else:
return render(request, 'edit_profile.html', c)
works!

Simple datastore entity with 2 fields that are also unique

All I am trying to produce is an entity that holds a unique username, and a unique device ID, and the ability to return an error if either of these conditions are not met on submission.
The only way I can see is to perform a query within a transaction, then filter the results. This however requires an ancestor (which seems unnecessary for a single simple entity).
What is the best method to go about doing this?
Here is an example that does what you want.
I put 2 entities to show you also how to make relationships
class Person(ndb.Expando):
registration_date = ndb.DateTimeProperty(auto_now_add=True)
#property
def info(self):
info = PersonInfo.query(ancestor=self.key).get()
return info
class PersonInfo(ndb.Expando):
email = ndb.StringProperty()
nick_name = ndb.StringProperty()
edit_date = ndb.DateTimeProperty(auto_now=True)
Later in the controller for register:
class RegisterPersonHandler(webapp2.RequestHandler):
def get(self):
user = users.get_current_user() #Stub here
if not user:
self.redirect(users.create_login_url(self.request.uri), abort=True)
return
person = Person.get_or_insert(user.user_id())
if not self._register(person, user):
# more logging is needed
logging.warning('Warning registration failed')
return
#ndb.transactional()
def _register(self, person, user):
''' Registration process happens here
'''
# check if the person has info and if not create it
info = PersonInfo.query(ancestor=person.key).get()
if not info:
info = PersonInfo(id=user.user_id(), parent=person.key)
info.nick_name = user.nickname()
info.email = user.email()
info.put()
return True
To answer also the comment question:
How can you programatically tell whether the returned entity is a new
or existing one though?
Try checking against a property that is default. Eg creation_date etc.
Though you can also check on something you need or on another entity's existence like I do because I expect the data to be consistent, and if not then create the bond.

Django form on the model

I have Guest model in my app:
class Guest(models.Model):
event = models.ForeignKey(Event, related_name='guests')
contact = models.ForeignKey(Contact, related_name='guests')
attending_status = models.CharField(max_length=32, choices=ATTENDING_CHOICES, default='no_rsvp')
number_of_guests = models.SmallIntegerField(default=0)
comment = models.CharField(max_length=255, blank=True, default='')
updated = models.DateTimeField(blank=True, null=True)
Event and Contact I fill up by myself in Admin when creating a guest. On the site all I want is a guest to fill up the form where he refreshes his attending status, points out number of guests and leaves a comment.
class RSVPForm(forms.Form):
attending = forms.ChoiceField(choices=VISIBLE_ATTENDING_CHOICES, initial='yes', widget=forms.RadioSelect)
number_of_guests = forms.IntegerField(initial=0)
comment = forms.CharField(max_length=255, required=False, widget=forms.Textarea)
How can I save the changes to the Guest model instance? How can I access the guest's id when saving the changes to his profile?
You are probably looking for Django's ModelForms. Instead of subclassing forms.Form in your RSVPForm class, you should subclass forms.ModelForm. Then you can use the features of the model form class to help you achieve what you want (hide some fields, etc). An example is below:
class RSVPForm(forms.ModelForm):
class Meta:
model = Guest
fields = ('attending_status', 'number_of_guests', 'comment')
This will do essentially what you want, but you will need to remember to provide an instance keyword argument to the form constructor in your view function. This argument is the instance of the Guest model you will be updating with the form.
form = RSVPForm(instance=guest_object)
Now calls to the form.save() method will automatically save the new data to this Guest object instance. You just need to make sure that you always pass the instance keyword argument, even when using request.POST:
form = RSVPForm(request.POST, instance=guest_object)

overwrite a model entry

can anybody gimme any help about how to overwrite a model entry.
i have latitude, longitude, and status fields in my model.
once i save the data i cant update or change it from my custom template.
how to update or overwrite these fields.
this is my views.py
def status_change(request):
if request.method == "POST":
rform = registerForm(data = request.POST)
if rform.is_valid():
register = rform.save(commit=False)
register.user = request.user
register.save()
return render_to_response('home.html')
else:
rform = registerForm()
return render_to_response('status_change.html',{'rform':rform})
and this is my Forms.py
class registerForm(forms.ModelForm):
class Meta:
model=register
fields = ('latitude', 'longitude', 'status')
Assigning a PK already in use in the database will overwrite that row completely when the model is saved. Or you can retrieve a model from the database, change the fields other than the PK, and save it.
this line solved my problem
def status_change(request):
instance = get_object_or_404(register,pk=request.user.id)
#rest of the code here

Resources