I have a model where I am using Enum for choices:
class Agreement(models.Model):
class Category(enum.Enum):
EULA = 0
PROVIDER = 1
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
category = models.IntegerField(
choices=[(choice.name, choice.value)
for choice in Category])
title = models.CharField(max_length=128)
content = models.TextField()
I register it using simple admin site registration:
admin.site.register(Agreement)
When admin site renders the object it doesn't allow me to save it? Has anyone had a similar issue?
According to the documentation:
The first element in each tuple is the actual value to be set on the model, and the second element is the human-readable name.
name and value should be the other way around, like this:
category = models.IntegerField(
choices=[(choice.value, choice.name)
for choice in Category])
because category is an integer field and name returns a string.
Related
I'm working on a site using Django and need help with data flow and accessing the foreign key data.
models.py
class Boards(models.Model):
board_no = models.CharField('Board No', max_length=10, unique=True)
board_name = models.CharField('Board Name', max_length=100)
class BoardLocations(models.Model):
board = models.ForeignKey(Boards, on_delete=models.CASCADE, related_name='board')
location_code = models.CharField('Location Code', max_length=10, null=True)
class BoardContacts(models.Model):
board_location = models.ForeignKey(BoardLocations, on_delete=models.CASCADE)
contact_first_name = models.CharField('Contact First Name', max_length=100, null=True)
contact_last_name = models.CharField('Contact Last Name', max_length=100, null=True)
The BoardContacts are tied to a BoardLocation which all come from a Board. I'm displaying the list of BoardContacts properly through a django-tables2 as follows:
List of Contacts
The URL shows:
http://127.0.0.1:8000/adm/boards/view_data_contact_list/6/
where 6 is Board.id. All fine. The Contact First Name link is defined as:
tables.py
class BoardContactsTableView(tables.Table):
selection = tables.CheckBoxColumn(accessor='id')
contact_first_name = tables.LinkColumn('view_board_contact', args=[A('id')], attrs=TABLE_VIEW_ATTRS)
contact_last_name = tables.LinkColumn('view_board_contact', args=[A('id')], attrs=TABLE_VIEW_ATTRS)
class Meta:
model = BoardContacts
template_name = TABLE_TEMPLATE_NAME
attrs = TABLE_ATTRIBUTES
ordering = ['contact_last_name']
fields = ('selection', 'contact_first_name', 'contact_last_name', 'contact_type', 'access_type', 'last_login', 'create_date')
When I select Contact First Name or Last Name, the URL changes to:
http://127.0.0.1:8000/adm/boards/view_data_contact/29/
because source indicates:
<td ><a class="text-decoration-none fw-bold" style="color:gray" href="/adm/boards/view_data_contact/29/">Robert</a></td>
But 29 is the BoardContacts.id. I need to maintain the Board.id of 6 within the URL so I can go back to the list of contacts using the Board Contacts button on the web page. Ideally, it should look like this:
http://127.0.0.1:8000/adm/boards/view_data_contact/6/29/
So, how do I make changes to LinkColumn within tables.py to include 6 and 29?
Instead of rendering as a LinkColumn, you can use a regular Column and build the full url in a render function.
https://django-tables2.readthedocs.io/en/latest/pages/custom-data.html#table-render-foo-methods
def render_contact_first_name(self, record, value):
url = reverse("view_board_contact", args=(record.board_location.board.id, record.id))
return mark_safe(f'{value}')
models.py
class products(models.Model):
name = models.CharField(max_length=100)
sku = models.CharField(max_length=50)
vendor = models.CharField(max_length=50)
brand = models.CharField(max_length=50)
price = models.FloatField()
product_status = models.BooleanField()
quantity = models.IntegerField()
def __str__(self):
return self.name
# categories
class categories(models.Model):
category_name = models.CharField(max_length=50)
parent_id = models.IntegerField()
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE)
category = models.ForeignKey(categories, on_delete=models.CASCADE)
def __str__(self):
return self.category
I can access 'category' table data(inside django shell) using
data = products.objects.all()
data.values('product_categories__category__category_name')
output: <QuerySet [{'product_categories__category__category_name': 'xxxx'}}]>
If I put this(inside django shell)
data.product_categories.category
output: 'QuerySet' object has no attribute 'product_categories'
How do I get a queryset(can be passed to html) which includes data from "categories" table along with the data of "products" table
There are a couple of issues happening here. First, data is a queryset, which is kind of like a list of objects, even though here there's just one object in the list. What you want is to get an attribute off of the item in the list, so you need something like a data.first() to get to that object before you start dotting into its attributes.
Secondly, the way Django handles reverse FK relationships requires that you refer to the FK by the standard name of, in your case, product_categories_set, OR you set your own related_name attribute on the FK. Something like:
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE, related_name='product_categories')
category = models.ForeignKey(categories, on_delete=models.CASCADE, related_name='product_categories')
def __str__(self):
return self.category
so that you can refer to your product_categories model from both the product and categories using just data.product_categories.
Thirdly, when accessing a reverse FK relationship, just like in point (1) above, you will get a related manager, from which you can get a queryset of items. Thus, to get the category name, you need to indicate which item you want the category name for. Assuming it's just the first item for everything, it would look something like:
data = products.objects.all()
product_category = data.product_categories.all()
category_name = product_category.category.category_name
Of course once you have more data, you'll not always want to just pick the first item, so you'll need to add filtering logic into the query to make sure you get the item you're looking for.
ETA, I do agree with the comment by Jorge above - a MTM would make this a bit simpler and would, in essence, create your product_categories table for you.
I am writing a form to let a user enter a purchase from the template. A couple things need to happen:
the purchase goes to populate a row in the replenishment table
some fields of the replenishment table get updated based on what the user has input
here is what my model look like:
class replenishment(models.Model):
Id = models.CharField(max_length=100, primary_key=True, verbose_name= 'references')
Name = models.CharField(max_length=200)
Quantity = models.FloatField(default=0)
NetAmount = models.FloatField(default=0)
SupplierID = models.CharField(max_length=200)
Supplier = models.CharField(max_length=200)
SellPrice = models.FloatField(default=0)
StockOnOrder = models.FloatField(default=0)
StockOnHand = models.FloatField(default=0)
def __str__(self):
return self.reference
and the form:
class ProcurementOperationRecord(forms.Form)
Id = forms.CharField(required=True)
Quantity = forms.FloatField(required=True)
NetAmount = forms.FloatField(required=True)
Supplier = forms.CharField(required=True)
SellPrice = forms.FloatField(required=True)
I have no clue how to let the user input the values in form and automatically add Quantity to StockOnOrder as well as automatically recognize the SupplierID based on Supplier. At this point I don't know where to start really. At least, is it possible to achieve what I try to do?
First, I've changed some things around and added some comments to what and why I did them.
# models/classes in python are singular AND camel cased (99.9%)
class Supplier(models.Model):
...
# models/classes in python are singular AND camel cased (99.9%)
class Replenishment(models.Model):
# attributes are normally lower case and snake cased (99.9%)
# try not to do this, a CharField??, unless you're using a guid? if so use UUIDField()
# https://docs.djangoproject.com/en/3.1/ref/models/fields/#uuidfield
id = models.CharField(db_column='Id', max_length=100, primary_key=True, verbose_name='references')
name = models.CharField(db_column='Name', max_length=200)
quantity = models.FloatField(db_column='Quantity', default=0)
net_amount = models.FloatField(db_column='NetAmount', default=0)
# deleted your field "Supplier" -- with this change you can join to the other table and get what you need without having to duplicate anything
supplier = models.ForeignKey(Supplier, db_column='SupplierID')
sell_price = models.DecimalField(db_column='SellPrice', default=0, max_digits=6, decimal_places=2) # You're asking for trouble if you keep this as FloatField
stock_on_order = models.IntegerField(db_column='StockOnOrder', default=0) # how can you have ordered a .5 for your stock? changed to IntegerField
stock_on_hand = models.IntegerField(db_column='StockOnHand', default=0) # how can you have a .5 of your stock? changed to IntegerField
class Meta:
db_table = 'replenishment' # try not to do this either.. let django come up with the name.. unless you're using an existing database/table?
...
# models/classes in python are singular AND camel cased (99.9%)
# django has a standard that they normally postfix forms with "Form" at the end of the class (no matter if it's a ModelForm or regular Form)
class ProcurementOperationRecordForm(forms.ModelForm)
class Meta:
model = Replenishment
fields = ('id', 'quantity', 'net_amount', 'supplier', 'sell_price')
# I would remove the "id", the client shouldn't care or know about it..
Now to create and update. (This would live inside a view)
# creating?
form = ProcurementOperationRecordForm(data=request.POST)
if form.is_valid():
form.save()
return redirect(..) or render(..)
# updating?
replenishment = Replenishment.objects.get(id='...something')
form = ProcurementOperationRecordForm(data=request.POST, instance=replenishment)
if form.is_valid():
form.save()
return redirect(..) or render(..)
This is just a general idea. You can try something like this.
First get the user input values of quantity and supplier like this from the valid form.
quantity = form.cleaned_data.get('quantity')
supplier = form.cleaned_data.get('supplier')
Then you can update your replenishment model
replenishment.objects.filter(Supplier=supplier).update(StockOnOrder=quantity)
I'm very confused about this right now,
so I know when there's a simple code like the below
def text_detail(request ,course_pk, step_pk):
step = get_object_or_404(Text, course_id = course_pk, pk=step_pk)
course_pk and step_pk from the url, and those requests are set equal to course_id and pk here. but what I don't understand is what is course_id and pk here? I mean, course_id is from Course model which is foreignkey to step. so it's self.Course.id so it's course_id. But then, how about the next one pk? shouldn't it be step_id = step_pk? when it's just pk how does django know which pk it is?
Sorry if the question is very confusing, I'm very confused right now.
Edit
class Step(models.Model):
title = models.CharField(max_length=200)
description = models.CharField()
order = models.IntegerField(default=0)
course = models.ForeignKey(Course)
class Meta:
abstract = True
ordering = ['order',]
def __str__(self):
self.title
class Text(Step):
content = models.TextField(blank=True, default="")
Actually the get_or_404() method doing a similar/exact job as below,
try:
return Text.object.get(pk=step_pk,course_id = course_pk)
except Text.DoesNotExist:
raise Http404
You can read the source code of the same here
What is course_id and pk ?
Both are attributes of your Text model, as the name indicates pk is your Primary Key of Text model and course_id is the id/pk of course field which is a FK.
EDIT
Text is inherited from Step model so, it will show properties of usual python class.Hence, the Text model be like this internally (not-exact)
class Text(models.Model):
content = models.TextField(blank=True, default="")
title = models.CharField(max_length=200)
description = models.CharField()
order = models.IntegerField(default=0)
course = models.ForeignKey(Course)
class Meta:
ordering = ['order', ]
def __str__(self):
return self.title
Example
text = Text.objects.get(id=1) # text instance with id=1
text.course_id # will hold the id of "course" instance which is related to the particular "text" instance
URL assignment and all those stuffs are entirely depends on your choice and logic. So If you need to get a Text instance in your view, do as below,
text = get_object_or_404(Text, pk = pk_of_TEXT_instance)
In Django, I have the following models.py
class Product(RandomPrimaryIdModel):
title = models.CharField(max_length=20, blank=True, null=True)
price = models.CharField(max_length=20, blank=True, null=True)
condition = models.CharField(max_length=20, blank=True, null=True)
class Mattress(Product):
length = models.CharField(max_length=50)
size = models.CharField(max_length=5)
class Pillow(Product):
shape= models.CharField(max_length=50)
comfort= models.CharField(max_length=5)
The idea is that there's a "product" model and several "product_type" models. I'm trying to create a database scheme that relates the two. The end goal is so that when I given access to a primary id for an object whose product_type is unknown, I can simply query/filter that object to find out what the product_type is of the object.
I know that sounds a bit confusing, but how would I go about implementing the correct way? The current scheme (the one above) is not the correct solution I believe.
According to the docs on multi-table inheritance you can reference the lowercase name of the model. In your case to find out the "product type" you'd do something like:
product = Product.objects.get(id=12)
try:
mattress = product.mattress
is_mattress = True
except Mattress.DoesNotExist:
is_mattress = False
You could abstract this out to a helper method that would do the tests for you and return the type as a string or enum of some sort.
If you have a reference to an object, can't you use something like:
p = Product.objects.get(id=1)
class_of_p = str(p.__class__)
and then parse the resulting string
"<class 'whatever.models.Pillow'>"
to find what you need? Apologies if I'm missing something.