Adding an item to django-cart (through ./manage shell) - django

I would like to add an Item entry into my database in my django app, but I am having problems. I'm still learning django (who isn't?), but I've done db entries before. Part of this is because of things like cart instance, and contenttype instances.
Generally I start with...
item1 = Item(Cart(...), ContentType(...), quanity='4',<etc.>)
And depending on what I put in, it will let me do that, but when I do item1.save(), it yells at me, and unfortunately the stack trace is hardly helpful. Or, maybe it's just me.
Any suggestions?

First suggestion is to post the stacktrace or even just the main exception; it's always more helpful to know what it's yelling.
My guess is first that you are passing in positional arguments and the model doesn't know what to do with which argument.
My second guess is that you are passing in unsaved instances Item(Cart()...) to foreign key fields that are non nullable so django or the database would complain if you didn't pass in an actual Cart instance with an ID defined.
So, explicitly define which fields you are passing to the constructor, and ensure you are passing in saved instances (not Cart() but Cart.objects.get(id=X))
cart = Cart.objects.latest('id')
item = Item(cart=cart, etc.)
item.save()

Related

Override vs extend Django's models.Manager to handle deleted objects (best practice)

There is a requirement, that nothing should be deleted from database (no rows should be deleted)
So, obviously, all models should be inherited from something like this:
class BaseModel(models.Model):
is_deleted = models.BooleanField(default=False)
But, it is not obvious how to make models.Manager to handle is_deleted the best way
I can imagine two options:
1) Override BaseModel's Manager's ._get_query_set() method
So, both will return only active objects (marked as is_deleted=False):
Article.objects.all()
Article.objects.filter(id__in=[1, 2])
Even .get(...) will raise 404 if is_deleted=True:
Article.objects.get(id=1)
Also, extend with additional method, to be able to actually access is_deleted=True:
Article.objects.deleted(id=1)
2) Second option is to extend BaseModel with additional second Manager, let's say - actual
So, all three will exclude objects with is_deleted=True:
Article.actual.all()
Article.actual.filter(id__in=[1, 2])
Article.actual.get(id=1) # 404 even if in db, but is_deleted=True
At the same time, regular objects works and stands with native behaviour (ignore is_deleted or not):
Article.objects.all()
Article.objects.filter(id__in=[1, 2])
Article.objects.get(id=1)
Maybe there are another good options? Is there a best practice?
Big thx for advices!
1 or 2 options?
From the django docs:
If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they’re defined in the model) has a special status. Django interprets the first Manager defined in a class as the “default” Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. As a result, it’s a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_queryset() results in an inability to retrieve objects you’d like to work with.
Also any third party apps you use, will also likely use the default manager. Ask yourself if it is important for any of these apps to access any of your 'deleted' rows.
For the above reason I think I would probably opt for the two managers option.
Another consideration
When you say "nothing should be deleted from database" do you mean that no rows should be deleted, or no data should ever be removed. If the later, remember that when you update a row, that old data is lost forever, and in that sense the data is 'deleted'.
To avoid this you can have a system where you only ever add rows to your database. You would need a non-unique id field to identify which rows you use, and when you get a particular id, you chose the most recently updated row with that id. Just a thought.

Add relations before adding them to DB in django

If I have a relation like this in django
class Reporter(models.Model):
pass
class Article(models.Model):
reporter = models.ForeignKey(Reporter)
and I want to create a new reporter with articles at once, I first have to save() the Reporter to DB and then I can add the articles.
But sometime, I would like to prepare everything "offline" (in sense of, before pushing anything to the DB), so like creating a Reporter object, adding articles to it and maybe afterwards still modifying some attributes of the Reporter object.
Then, when everything is done, I want to push all together to the DB. But of course when I use Reporter.article_set.add() before calling Reporter.save() I will get an error, because django will try to add the articles and foreign keys to the DB automatically. Is there any way to prevent this, and prepare my object inlcuding the relations "offline" ?
My own approach would be, to add a set_articles method to Reporter and then override the save() method so it will check if there are any articles set and add them after saving the Reporter
But before I start improvising I would like to know if there are already any solutions within django
The save() method accepts an optional commit keyword argument, which accepts either True or False. If you call save() with commit=False, then it will return an object that hasn’t yet been saved to the database. In this case, it’s up to you to call save() on the resulting model instance.

Keeping form choice list up-to-date in django

I have a form in my admin site where the user is to select a single object from a particular model from a dropdown.
thing_choices = [(x.id, x) for x in Things.objects.all()]
class ThingSelector(forms.Form):
thing = forms.ChoiceField(choices=thing_choices)
If I first add a new Thing object, then go to the page with the selector form, I find that the object does not appear in the dropdown. This is presumably the form was populated with choices when I first stood the server up. Testing bears this out, because if I restart Django, the new choice appears on the list.
How can I get around this so that I can create objects and have them appear on this form too?
(More info: the selected thing is submitted with the form in order for processing to be done upon it.)
Thanks...
If I first add a new Thing object, then go to the page with the selector form, I find that the object does not appear in the dropdown. This is presumably the form was populated with choices when I first stood the server up.
Correct, the variable thing_choices is calculated when your code is first run, and if its at the same scope as your form its unlikely to ever run again.
An easier way is to use a ModelChoiceField, which references a model, rather than a ChoiceField. Like so:
class ThingSelector(forms.Form):
thing = forms.ModelChoiceField(queryset=Things.objects.all()
This should mean that as new Thing objects are added, they are able to be selected in the form.

Use a form inside a form or How to work with foreign keys in forms

I am going to use the documentation model as an example:
class Car(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# ...
class Manufacturer(models.Model):
# ...
Let's say I want to create a form to add a new Manufacturer, and in this form I want to be able to add new cars. How would it be done with django-forms?
Is it even possible?
Thank you in advance for your help!
The short answer:
You want modelformset_factory, documented here: http://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#model-formsets
The still-rather-short answer, but with a couple of gotchas to watch for:
On the processing side, if you're creating both the Manufacturer and the multiple Car instances, you'll want to be sure to save the manufacturer first, before saving the individual cars (which must reference the manufacturer). Make sure this happens in a database transaction if you can help it.
One more note: if this is a bit confusing to you, beat in mind that there's no hard and fast rule saying that you can only process one form in a request. You just have multiple forms.Form (or subclasses thereof) objects within the HTML <form> tag, which posts to a single request location that processes each form individually and saves them out. Again, use a database transaction so that if something fails at the end, the entire thing gets rolled back and the user can correct their error without having bad or orphan data in the database.

Specifying default value for django hidden form field - bone DRY?

So let's say at the last minute (in the view) I decide I want to specify a default for a field and make it hidden, like so:
form.fields['coconut'] = forms.ModelChoiceField(
label="",
widget=forms.HiddenInput(),
queryset=swallow.coconuts.all(),
initial=some_particular_coconut,
)
My question is this: Do I really need to specify queryset here? I mean, I already know, from initial, exactly which coconut I'm talking about. Why do I also need to specify that the universe of available coconuts is the set of coconuts which this particular swallow carried (by the husk)?
Is there a way I can refrain from specifying queryset? Simply omitting causes django to raise TypeError.
If indeed it is required, isn't this a bit damp?
I think is good that stackoverflow answers point to the 'right' way to do things, but increasingly the original question goes unanswered because the user was trying to do the wrong thing.
So to answer this question directly this is what you can do:
form.fields['coconut'] = forms.ModelChoiceField(label="", widget=forms.HiddenInput(attrs={'value':some_particular_coconut}), queryset=swallow.coconuts.all())
Notice the named argument passed to HiddenInput, its super hackish but its a direct answer to the original question.
The problem is that you're trying to set up a hidden ModelChoiceField. In order to have a Choice (dropdown, traditionally) it needs to know its Choices - this is why you give a queryset.
But you're not trying to give the user a choice, right? It's a hidden input, and you're setting it from the server (so it gets POSTed back, presumably).
My suggestion is to try to find a way around using the hidden input at all. I find them a bit hacky. But otherwise, why not just specify a text field with some_particular_coconut.id, and hide that? The model's only wrapping that id anyway.
The reason django requires a queryset is because when you render the field to the page, django only sends the id. when it comes back, it needs knowlege of the queryset in order to re-inflate that object.
if you already know the queryset at form creation time, why not simply specify form.fields['coconut'].initial = some_particular_coconut in your view and leave the rest of the definition in your forms.py?
If you find that you only really need to send the id anyway (you don't have to re-inflate to an object at your end), why not send it in a char field?