Django create model object and list without saving it - django

I have for example something like this:
class Order(models.Model):
user= models.ForeignKey(User)
class OrderLines(models.Model):
product = models.ForeignKey(Product)
quanity = models.PositiveSmallIntegerField()
order = models.ForeignKey(Order)
Now on my page want to do something like that (maybe my approach in design is wrong):
I receive POST data with product_id and quanity and store it as simple dictionary in session as 'ORDER_LINES' variable
When user clicks "Make order" I want to create Order object simply:
(I'm using generic views)
class ShowOrderPreview(TemplateView):
template_name = "shop/order_preview.html"
http_method_names = ['get']
def get_context_data(self, **kwargs):
context = super(ShowOrderPreview, self).get_context_data(**kwargs)
order = Order(user = self.request.user)
for product, quanity in self.request.session.get('ORDER_LINES', {}):
order.orderlines_set.add( product=get_object_or_404(Product, pk=product_id), quanity=quanity)
context['tmp_order'] = order
return context
I dont want to that order be stored in database and only after user confirmation create it again and save.
I'm receiving: "Column 'order_id' cannot be null" on line order.orderlines_set.add(...
I know that the fastest solution will be create something like DummyOrder and DummyOrderLines with structure like the sam like my main Order and OrderLines object
I'm using Django 1.4.1.

Related

unique id generator in a custom format in django

i want to generate an unique id code in my model
for example -
id want to generate
Can anyone Kindly help me in the same?
i Want id in the table as shown below-
class parameter(models.Model)
name=models.models.CharField(max_length=50)
type=model.#should come from another table
subtype=model.#should come from another table
id= # should compile all the above as in picture
thanks
I think you should not store the unique id in DB, because you can easily generate it by a property method:
class Parameter(models.Model)
name=models.models.CharField(max_length=50)
type=model.ForeignKey(Type)
subtype=model.ForeignKey(SubType)
product_type=model.ForeignKey(ProductType)
serial_no = models.CharField()
#property
def generated_id(self):
return '{}/{}/{}/{}'.format(self.type_id, self.subtype_id, self.product_type_id, self.serial_no.zfill(2))
If you intend to display it in admin site, then simply try like this:
#admin.register(Parameter)
class ParameterAdmin(admin.ModelAdmin):
model = Parameter
fields = ['type', 'sub_type', 'product_type', 'serial_no', 'generated_id']
readonly_fields = ('generated_id',)
Update
If you want to store in in DB, then you need to override the save method of the models. Like this:
class Parameter(models.Model)
name=models.models.CharField(max_length=50)
type=model.ForeignKey(Type)
subtype=model.ForeignKey(SubType)
product_type=model.ForeignKey(ProductType)
serial_no = models.CharField()
generated_id = models.CharField()
def generate_id(self):
return '{}/{}/{}/{}'.format(self.type_id, self.subtype_id, self.product_type_id, self.serial_no.zfill(2))
def save(self, *args, **kwargs):
self.generated_id = self.generate_id()
super().save(*args, **kwargs)

Django field choices not properly updating

I have two models, one that loads the other model it's titles in a choice field dynamically. I fixed it so far that if I add a new object to the model which the titles are used from by updating the choice list in the init method, the choice list gets updated immediately. However when I decide to choose it as option and save it I get: Select a valid choice. example is not one of the available choices. When I restart the server it does work, what I did:
model:
class Assessment(models.Model):
title = models.CharField(max_length=200)
SPECIFIC_REQUIREMENTS_CHOICES = ()
SPECIFIC_REQUIREMENTS_CHOICES_LIST = []
for sRequirement in SpecificRequirements.objects.all():
SPECIFIC_REQUIREMENTS_CHOICES_LIST.append((sRequirement.title, sRequirement.title))
SPECIFIC_REQUIREMENTS_CHOICES = SPECIFIC_REQUIREMENTS_CHOICES_LIST
sRequirementChoice = models.CharField(max_length=200, choices=SPECIFIC_REQUIREMENTS_CHOICES,
default='')
forms:
class AssessmentForm(forms.ModelForm):
class Meta:
model = Assessment
fields = ['title', 'sRequirementChoice']
def __init__(self, *args, **kwargs):
super(AssessmentForm, self).__init__(*args, **kwargs)
SPECIFIC_REQUIREMENTS_CHOICES_LIST = []
for sRequirement in SpecificRequirements.objects.all():
SPECIFIC_REQUIREMENTS_CHOICES_LIST.append((sRequirement.title, sRequirement.title))
SPECIFIC_REQUIREMENTS_CHOICES = SPECIFIC_REQUIREMENTS_CHOICES_LIST
self.fields['sRequirementChoice'].choices = SPECIFIC_REQUIREMENTS_CHOICES
That's not how Model choices work. You are not supposed to populate choices dynamically in models.
You should consider using a ForeignKey relation with SpecificRequirements in your model.

Using field values from a ForeignKeyed object for ModelChoiceFields

My friends and I play a spreadsheet-based sports picking game which is very tedious to make changes to. I've wanted to learn Django for a while so I've been working on creating it as a webapp. Here are the models I'm working with:
class Sheet(models.Model):
user = models.ForeignKey(User)
... other stuff
class Game(models.Model):
home_team = models.CharField(max_length=100, default='---')
away_team = models.CharField(max_length=100, default='---')
... other stuff
class Pick(models.Model):
sheet = models.ForeignKey(Sheet)
game = models.ForeignKey(Game)
HOME = 'H'
AWAY = 'A'
PICK_TEAM_CHOICES = (
(HOME, 'Home'),
(AWAY, 'Away'),
)
pick_team = models.CharField(max_length=4,
choices=PICK_TEAM_CHOICES,
default=HOME)
... other stuff
Right now, I'm trying to nail down a simple way to display the following form with information from a foreign keyed model instead of the pick_team default choices. The game is hidden because it's paired with the generated PickForm via use of the initial functionality in the view.
class PickForm(ModelForm):
class Meta:
model = Pick
widgets = {'game': forms.HiddenInput()}
fields = ['sheet','game','amount','pick_type','pick_team']
def __init__(self, *args, **kwargs):
game = kwargs['initial']['game']
super(PickForm, self).__init__(*args, **kwargs)
self.fields['pick_team']=forms.ModelChoiceField([game.away_team,game.home_team])
From what I can tell, the ModelChoiceField expects a queryset- so when I provide a list or a tuple, I get a 'list' object has no attribute 'iterator' error. Knowing this now, how can I display the Game fields home_team and away_team in the pick_team dropdown on the template? Currently it defaults to 'Home' and 'Away'.
I know this is a common question at the core- how to display ForeignKeyed information in a dropdown, however all the answers I've found are fine with providing a queryset to ModelChoiceField, because they're typically trying to list a field from every object (or some filtered subset). In this case, I only want to list 2 fields, and only from one object.
I tried returning a queryset consisting of the Game object already present in kwargs, but it just displays the game's str() method in the dropdown, and attempting to refine the queryset with the relevant field names isn't working either.
EDIT: I realized that actually using the home_team and away_team values from the Game object would require extra processing on saving the Pick, or possibly be harder than that. Is there any way to do this sort of aliasing in the template alone? Similar to how with choice fields I can use get_pick_team_display to show a nicer looking display value ('Home', 'Away') instead of the vague 'H' or 'A'.
EDIT2: View code
class GameDetail(DetailView):
#model = Game
template_name = 'app/games.html'
context_object_name = 'game_detail'
def get_object(self):
game = get_object_or_404(...object filtering)
return game
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
pick = Pick.objects.get(game=context['game_detail'],
....other stuff)
context['pickform'] = PickForm(initial={'game':context['game_detail'],
.... other stuff
except Pick.DoesNotExist:
#pick = none
context['pickform'] = PickForm(initial={'game':context['game_detail'],
})
return context
def post(self, request, *args, **kwargs):
form = PickForm(request.POST)
if form.is_valid():
....process form
This is a quickfix approach. In your __init__, instead of reassigning pick_team field, just redefine its options as follows:
self.fields['pick_team'].choices = (
('H', game.home_team),
('A', game.away_team),
)

How to filter a QuerySet with relation fields compared to a (dynamic) list

I have a Requests Model which has a one-to-many relation to its RequestDetails. I also have a RequestFilter that has a one-to-one relation to the auth.user. What i'm trying to do is to display all Requests in a generic ListView, which have at least one RequestDetail that has a category enabled by the RequestFilter settings. I tried to accomplish this in different ways but still end up with a "Relation fields do not support nested lookups" Error. Here is what I did:
Given Models:
A User specifies the Requests (not django user.request) he wants to receive in a settings menu based on the following RequestFilter Model:
class RequestFilter(models.Model):
user = models.OneToOneField(User)
category1_enabled = models.BooleanField(...)
category2_enabled = models.BooleanField(...)
category3_enabled = models.BooleanField(...)
...
def get_categories_enabled(self):
returns a list of category names which are set to True e.g. ['category1', 'category3']
The Requests themselves contain basic information and a reference code:
class Requests(models.Model):
request_id = models.IntegerField(unique=True, ...)
reference = models.CharField(max_length=10)
some_general_info = models.CharField(...)
...
One Request can have many RequestDetails (like Orders which have many Products)
class RequestDetail(models.Model):
request = models.ForeignKey(Requests, to_field='request_id', related_name='details')
product_id = models.IntegerField()
category_id = models.IntegerField()
...
def get_category_name:
returns the name of the category of the RequestDetail e.g. 'category3'
I have a generic class based ListView which should display all Requests that contain at least one RequestDetail that is of a category which the User has set to enabled in the settings (RequestFilter).
views.py
class DashManagerRequestsView(generic.ListView):
template_name = 'dashboard/dashmanagerrequests.html'
context_object_name = 'request_list'
def get_categories_enabled(self):
return self.request.user.requestfilter.get_categories_enabled()
def get_queryset(self):
"""Returns all requests as an ordered list."""
requestfilter_list = self.get_categories_enabled()
return Requests.objects.order_by('-date_add').filter(details__get_category_name__in=requestfilter_list)
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(DashManagerRequestsView, self).dispatch(*args, **kwargs)
I also tried using an F() expression but this doesn't work because it only compares the values of two different fields on the SAME model instance. The closest I got was to get a list of the enabled categories for the user and lookup if the category name is a part of this list with:
return Requests.objects.order_by('-date_add').filter(details__get_category_name__in=requestfilter_list)
but this raises a Relation fields do not support nested lookups Error.
thanks to schillingt, who pushed me into the right direction i came up with a solution.
since it is not possible to use model methods for filtering a QuerySet i generate a list of allowed ids during execution outside the filter function by relating to the RequestDetail model method using a second for loop. of course this can also be done using a list comprehension or something like that:
def get_queryset(self):
queryset = Requests.objects.order_by('-ps_date_add')
request_ids = []
for request in queryset:
for detail in request.details.all():
if detail.get_category_name() in self.get_categories_enabled():
request_ids.append(request.id)
q = q.filter(id__in=request_ids)
return q
this may not be the best solution in terms of efficiency if it comes to large amounts of data.

Show complex entity in Django Admin interface

I'm stuck trying to figure how to do the following:
I have a few entities:
PurchaseItem (an item in user's cart),
Order (an order - combines one or many PurchaseItems),
OrderStatusHistory (that's status items for the Order - instead of changing, I create new ones to be able to retrospectively preview how status changed over time).
I don't want any of these to be created via admin - they are all created via public interface, but I have to show the Order and its attributes in the admin panel:
I need to be able to show list of orders. That's simple.
When I click on an order or something I want to be able to view the order's details:
list of Purchase items.
I need to be able to change the status of the order - selecting from a drop down or something - however, this action show be triggering a new statusHistory item creation.
Is this all possible with admin interface or should I forget about it and create my own implementation with pages and all?
My models look like this:
class Order(models.Model):
dateCreated = models.DateTimeField(null=False,default=datetime.now())
items = models.ManyToManyField(PurchaseItem)
user_name = models.CharField(null=True,blank=True,max_length=200)
phone = models.CharField(null=False,blank=False,max_length=11,validators=[validate_phone])
phone_ext = models.CharField(null=True,blank=True,max_length=5,validators=[validate_phone_ext])
email = models.CharField(null=False,blank=False,max_length=100,validators=[validators.EmailValidator])
addressCity = models.CharField(null=False,blank=False,max_length=100)
addressStreet = models.CharField(null=False,blank=False,max_length=200)
notes = models.TextField(null=True,blank=True)
accessKey = models.CharField(max_length=32,default=CreateAccessKey())
class PurchaseItem(models.Model):
picture = models.ForeignKey(Picture, null=False)
paperType = models.CharField(null=False,max_length=200)
printSize = models.CharField(null=False,max_length=200)
quantity = models.IntegerField(default=1, validators=[validators.MinValueValidator(1)])
price = models.DecimalField(decimal_places=2,max_digits=8)
dateCreated = models.DateTimeField(null=False)
cost = models.DecimalField(decimal_places=2,max_digits=8)
class OrderStatusHistory(models.Model):
orderId = models.ForeignKey(Order)
dateSet = models.DateTimeField(null=False,default=datetime.now())
status = models.IntegerField(choices=OrderStatus,default=0,null=False,blank=False)
comment = models.TextField(null=True,blank=True)
The following inline setup doesn't work because Order doesn't have a FK to PurchaseItems:
class OrderStatusHistoryAdmin(admin.StackedInline):
model = OrderStatusHistory
class PurchaseItemAdmin(admin.StackedInline):
model = PurchaseItem
class OrderAdmin(admin.ModelAdmin):
model = Order
inlines = [OrderStatusHistoryAdmin,PurchaseItemAdmin]
admin.site.register(Order,OrderAdmin)
Part 1
Use Inlines, that's very straight forward and django excels at this.
Part 2
Sure you could override your save for example and check if the drop down item has changed. If it has, generate your order status history object.
def save(self, *args, **kwargs):
if self._initial_data['status'] != self.__dict__['status']:
self.orderstatushistory_set.create("Status Changed!")
super(Order, self).save(*args, **kwargs)
You could do the same thing in the ModelAdmin too
def save_model(self, request, obj, form, change):
if obj._initial_data['status'] != obj.__dict__['status']:
# create whatever objects you wish!
Part 1:
You can 'nest' models with TabularInline or StackedInline admin models.
class OrderAdmin(admin.ModelAdmin):
model = Order
inlines = [
OrderStatusAdmin,
PurchaseItemAdmin
]
class OrderStatusAdmin(admin.StackedInline):
model = OrderStatus
class PurchaseAdmin(admin.StackedInline):
model = PurchaseItem
More information can be found here: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#inlinemodeladmin-objects
Part 2:
I need to be able to change the status of the order - selecting from a drop down or something - however, this action show be triggering a new statusHistory item creation.
For this you can use signals. There is a post_save and pre_save. So each time you save an order you can add extra logic. The pre_save signal has a sender and an instance so I think you can compare the status of the sender and the instance to be saved and if it changed you can add an other OrderStatus model.
More info can be found here:
http://docs.djangoproject.com/en/dev/ref/signals/#pre-save