I have the following code
VocabFromDatabase = Vocab.objects.filter(User = U, IsCard = True).order_by("LastStudyDate")
VocabFromDatabase[0].Mnem = "Passed"
VocabFromDatabase[0].save()
According the docs save() should save the changes, but it seems to silently fail. After some fiddling around it seems the problem isn't with save() but rather with assigning a value to a property of one of the objects from the queryset. However everywhere I look tells me to either use update() (which I think would update all the objects in the queryset, which I do not want), or that save() should work.
Is there something I'm not aware of, or something I'm doing wrong here?
Looks like setting the model's name field and calling the model's save method is being done to 2 separate references of the model. Everytime you select via [index], queryset is returning from its cache.
https://docs.djangoproject.com/en/2.1/topics/db/queries/#when-querysets-are-not-cached
queryset[0].name = "foo" // <Model pk=1 name="foo">
queryset[0].save() // <Model pk=1 name=default> SAVED
Related
I am new to Django.
I want to save the queried instance of Model-A,'Q' into Model-B. Model-A corresponds to Database D1
and Model-B to D2.
In simple terms something like:
Q=queryset(A).using('D1')
Q.save(database='D2',model='Model-B')
I this case, ‘A’ and ‘B’ are not the same kind of models, but the queryset used on ‘A’, returns the fields identical to those of ‘B’.
I realise this can be achieved by using a simple for loop to copy instance_A into instance_B and then use save(), but is there a way to do it by passing some arguments to save like save(model=‘B’, using=‘DB2’)?
Maybe check out Manually selecting a database for a QuerySet and Selecting a database for save.
My guess is that you should only use one model (if they have the same exact fields).
I'm not sure if the code below will work as is, but something like that.
queryset = A.objects.using('D1').all()
for instance in queryset:
instance.save(using='D2')
Is there a way to do the following:
asset, _ = Asset.objects.get_or_create(system=item['system'], system_table=item['system_table'], ...)
Asset.objects.filter(pk=asset.pk).update(**item)
And also call the .save() method? I think I've read somewhere that you can run an update on the actual instance and not go through the objects manager. How would that be done? Currently I'm doing the following, which is quite repetitive and inefficient:
a = Asset.objects.filter(pk=asset.pk).update(**item)
a.save()
Since you already have the asset object, you can just make use of it.
# assuming that you have a `Asset` object in `asset` variable, somehow
item = {"foo": "foo-value"}
for field, value in items.items():
setattr(asset, field, name)
asset.save()
You can also specify the update_fields parameter of save() method as
asset.save(update_fields=list(item.keys()))
The best way to do this is to just call save() directly. You will need to call get() instead of filter(), though.
Asset.objects.get(pk=asset.pk).save(update_fields=item)
This isn't a problem since your existing filter() is guaranteed to return a queryset with at most one Asset anyway. You just have to be sure that the given pk actually exists or wrap the get() call in a try...except block.
But...since you already have the Asset instance in asset, there's no reason to waste time with a DB query. Just call save directly on the object you have:
asset.save(update_fields=item)
Using Django 11 with PostgreSQL db. I have the models as shown below. I'm trying to prefetch a related queryset, using the Prefetch object and prefetch_related without assigning it to an attribute.
class Person(Model):
name = Charfield()
#property
def latest_photo(self):
return self.photos.order_by('created_at')[-1]
class Photo(Model):
person = ForeignKey(Person, related_name='photos')
created_at = models.DateTimeField(auto_now_add=True)
first_person = Person.objects.prefetch_related(Prefetch('photos', queryset=Photo.objects.order_by('created_at'))).first()
first_person.photos.order_by('created_at') # still hits the database
first_person.latest_photo # still hits the database
In the ideal case, calling person.latest_photo will not hit the database again. This will allow me to use that property safely in a list display.
However, as noted in the comments in the code, the prefetched queryset is not being used when I try to get the latest photo. Why is that?
Note: I've tried using the to_attr argument of Prefetch and that seems to work, however, it's not ideal since it means I would have to edit latest_photo to try to use the prefetched attribute.
The problem is with slicing, it creates a different query.
You can work around it like this:
...
#property
def latest_photo(self):
first_use_the_prefetch = list(self.photos.order_by('created_at'))
then_slice = first_use_the_prefetch[-1]
return then_slice
And in case you want to try, it is not possible to use slicing inside the Prefetch(query=...no slicing here...) (there is a wontfix feature request for this somewhere in Django tracker).
I'm using Django 1.8, Mezzanine, Cartridge, and I use Postgresql as the database.
I've updated the num_in_stock directly from the database. The quantities are all correct in the database but not on my website. I know the solution is here, but I don't know what to do with that. I really need it spelled out for me.
How exactly would you use this in Cartridge to refresh the num_in_stock?
This should be all you need to do to update one object. Replace object_name with your object.
object_name.refresh_from_db()
I assume you're using an F expression.
According to the documentation an F expression:
...makes it possible to refer to model field values and perform
database operations using them without actually having to pull them
out of the database into Python memory.
You're working directly in the database. Python knows nothing about the values of the model fields. There's nothing on memory, everything is happening on the database.
The documentation's example:
from django.db.models import F
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
Although reporter.stories_filed = F('stories_filed') + 1 looks like a
normal Python assignment of value to an instance attribute, in fact
it’s an SQL construct describing an operation on the database.
So, for Python to know about this value you need to reload the object.
To access the new value saved this way, the object must be reloaded:
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()
In your example:
object_name.refresh_from_db()
And one more thing...
F() assignments persist after Model.save()
F() objects assigned to
model fields persist after saving the model instance and will be
applied on each save().
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
reporter.name = 'Tintin Jr.'
reporter.save()
stories_filed will be updated twice in this case. If it’s initially
1, the final value will be 3. This persistence can be avoided by
reloading the model object after saving it, for example, by using
refresh_from_db().
I assume the num_in_stock is an attribute of your model class. If true you should get an instance of the class (i.e object_name) then
object_name.refresh_from_db()
After which, you can access it like object_name.num_in_stock
I have this manytomany field in my model that I have overridden with a CharField that receives a csv list of the second models name attribute.
class PostForm(ModelForm):
tests = CharField(label="tests")
class Meta:
model = Post
fields = ('title','body')
def clean_tests(self):
# Here I clean, create or retrieve and return a list of Test objects.
Now, saving and validating is alright with this code, everything works, my problem comes when I create the PostForm with an existing instance, like PostForm(instance=current_post).
The CharField should contain a csv list but it contains nothing, obviously this happens because there is no conversion happening from Test object list to test name list, the problem is I do not know where to put that code, I see no method I could override to get this done, i've looked into initial data and default properties of fields.
I'm not sure if there's a method you could override to do this -- from a look at the BaseModelForm constructor though, it looks perfectly okay to specify both the instance and initial keyword arguments together -- the instance is converted into a dict (subject to the fields and exclude options in Meta), and that dict's update method is called with initial. Something like this should work:
# build your csv list somehow (just speculation here)
tests = ','.join(test.name for test in current_post.tests.all())
form = PostForm(instance=current_post, initial={'tests': tests})