overwrite a model entry - django-models

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

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.

Serializer field for side effect model django rest framework

I have a django.db.models.Model A whose instances are created in a rest_framework.serializers.ModelSerializer from POST requests.
Depending on the data being sent in the POST, I would like to create one of several other "addon" models, let's say B or C, which I link to the original through a django.db.models.OneToOneField:
from django.db import models
class A(models.Model):
some_field = models.CharField()
class B(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
class C(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
What I would like to is to have a serializer which validates the incoming data, including some string indicating which addon to use. The serializer then creates the model instance of A and based on this creates the addon model.
I do not want to create a utility field in model A used to determine which addon to use, I would like to create the model directly using the instance of model A and information from the POST itself.
At the same time when accessing the data through a get, I would like to return the original string used to determine which addon to use.
What I have come up with so far:
from rest_framework import serializers
str2model = {'b': B, 'c': C}
class AddonField(serializers.Field):
def to_representation(self, value):
# I completely ignore "value" as no "internal value" is set in "to_internal_value"
myvalue = self.parent.instance.addon
for addon_name, addon_class in str2model.items():
if isinstance(myvalue, addon_class):
return addon_name
def to_internal_value(self, data):
# I create the "internal value" after "A" instance is created, thus here I do nothing?
return data
class ASerializer(serializers.ModelSerializer):
some_field = serializers.CharField()
the_addon = AddonField()
def validate_the_addon(self, value): # here addon is a string
if value in str2model.keys():
return value
def create(self, validated_data):
addon_name = validated_data.pop('the_addon')
addon_class = str2model[addon]
a = super(ASerializer, self).create(validated_data)
addon_class.objects.create(a=a)
return a
class Meta:
model = A
fields = ["some_field", "the_addon"]
When testing this I get:
AttributeError: Got AttributeError when attempting to get a value for field `the_addon` on serializer `ASerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `A` instance.
Original exception text was: 'A' object has no attribute 'the_addon'.
How can I temporarily store the_addon in the serializer until the A instance has been created?
This is how I would typically approach it
# Serializer
class ASerializer(serializers.Serializer):
some_field = serializers.CharField()
addon_b = serializers.CharField(required=False, allow_null=True)
addon_c = serializers.CharField(required=False, allow_null=True)
def create(self, validated_data):
addon_b = validated_data.pop('addon_b')
addon_c = validated_data.pop('addon_c')
a = A.objects.create(some_field=validated_data['some_field'])
if addon_b:
B.objects.create(a=a)
if addon_c:
C.objects.create(a=a)
return a
You can do other validations if necessary.
class TestAPIView01(generics.CreateAPIView):
permission_classes = {}
serializer_class = serializers.ASerializer
queryset = A.objects.all()
Also, look at the related_name on B and C you may want to consider making them different, as that might throw an error in the future. Cheers

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!

Allowing Edit to editable=False Fields in Django Admin

DRF will use the editable=False on a field to default the Serializer to read-only. This is a very helpful / safe default that I take advantage of (ie I won't forget to set the Serializer to read-only). That being said once I have set editable=False is there any way to then force the Django admin to allow editing one of those fields?
Presumably the admin is a super user and I do want him to be able to change the fields value but fore safety I want the default Serializer logic to be read only.
UPDATE
I don't actually need to be able to edit the field as much as "set-it" when I create the object.
You are going about this the wrong way.
Your models should be the most pure implementation of the things you are modelling. If something about a model is fixed (for example a creation date) it shouldn't be editable in the model, if its mutable, then leave as editable in the model.
Otherwise, in the future you (or someone else) might be stuck wondering why a field which is set to editable=False is some how being changed. Especially as the documentation states:
If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation.
If you have one view in which it shouldn't be editable (such as in the API), then override it there.
If you have multiple serilaizers for a model, instead make an abstract serializer with a read_only_fields set and then subclass that. For example:
class AbstractFooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
read_only_fields = ('bar',)
class MainFooSerializer(AbstractFooSerializer):
pass
class DifferentFooSerializer(AbstractFooSerializer):
pass
If you really, really want to use editable=False, but allow the item to be edited in the Admin site only on creation you have an up hill battle.
Probably the best approach would be to reimplement the AdminForm you are using for the Admin
So instead of:
class FooAdmin(admin.ModelAdmin):
Use:
class FooAdmin(admin.ModelAdmin):
form = MySpecialForm
Then declare the form:
class MySpecialForm(forms.Model):
def __init__(self, *args, **kwargs):
self.is_new = False
if kwargs.get('instance',None) is None:
# There is no instance, thus its a new item
self.is_new = True
self.fields['one_time_field'] = forms.CharField() # Or what have you.
super(MySpecialForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = super(MySpecialForm, self).save(commit)
if self.is_new:
instance.your_one_time_only_field = self.one_time_field
instance.save()
return instance
Note: you will need to manually add a field and save each readonly field that you want to do this for. This may or may not be 100% functional.
For those who want to allow editing of a non-editabled field only during creation (no instance.pk, yet):
# models.py
class Entity(Model):
name = CharField(max_length=200, unique=True, null=False, blank=False, editable=False)
# admin.py
#register(Entity)
class EntityAdmin(ModelAdmin):
def get_readonly_fields(self, request, obj=None):
if obj: # This is the case when obj is already created i.e. it's an edit
return ['id', 'name']
else:
return []
# this override prevents that the new_name field shows up in the change form if it's not a creation
def get_form(self, request, obj=None, **kwargs):
orig_self_form = self.form
if not obj:
self.form = CreateEntityForm
result = super().get_form(request, obj=obj, **kwargs)
self.form = orig_self_form
return result
# forms.py
class CreateEntityForm(ModelForm):
new_name = CharField(max_length=200, min_length=2, label='Name', required=True)
def clean_new_name(self):
code = self.cleaned_data['new_name']
# validate uniqueness - if you need
exists = Entity.objects.filter(name=code).first()
if exists:
raise ValidationError('Entity with this name already exists: {}', exists)
return name
def save(self, commit=True):
if self.instance.pk:
raise NotImplementedError('Editing of existing Entity is not allowed!')
self.instance.name = self.cleaned_data['new_name'].upper()
return super().save(commit)
class Meta:
model = Entity
fields = ['new_name']
exclude = ['id', 'name']

django i can't save form with 2 foreign keys

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

Resources