So I have a question I was thinking of creating a single table that has a foreign key to several other tables, and using another field "type" to say what table the key should belong to.
class Status(Models.model):
request = models.ForeignKey("Request1", "Request2", "Request3")
request_type = models.IntegerField()
...Some status related data
class Request1(Models.model):
...Some data
class Request2(Models.model):
...Some other data
Class Request3(Models.model):
...different data
My question is, is it possible to define a foreign key like this?
another solution I thought of was to define my model like this
class Status(Models.model):
request1 = models.ForeignKey("Request1")
request2 = models.ForeignKey("Request2")
request3 = models.ForeignKey("Request3")
...Some status related data
class Request1(Models.model):
...Some data
class Request2(Models.model):
...Some other data
Class Request3(Models.model):
...different data
But if I do it this way is it possible to define a constraint via django that says only 1 foreign key is allowed to have data and the other two must be null? or will I have to strictly set this constraint up on the db side.(I'm using postgres) I would like to be able to tell django to do it when it creates the db so I don't have to remember every time someone recreates the db.
Any input or advice would be greatly appreciated. I am not married to either of these ideas, so if there is another clever way to achieve the same effect i am up to hear it. Thank you for your time.
Edit: I am using django 1.7.10
You should use the contentypes framework in Django.
There's an example for a generic relation here :https://docs.djangoproject.com/en/1.8/ref/contrib/contenttypes/#generic-relations
For your requirement it could look something like this:
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Status(models.Model):
request_type = models.ForeignKey(ContentType)
request_id = models.PositiveIntegerField()
request = GenericForeignKey('request_type', 'request_id')
You can then do something like following:
status1 = Status(request=Request1("foo"))
status1.save()
status2 = Status(request=Request2("bar"))
status2.save()
status1.request // <Request1 "foo">
status2.request // <Request2 "bar">
Related
With a database model described in the simplified toy example below, we are trying to get a list of EAV attributes used for a specific Product.
The sample source code in the Actions section below serves the purpose; however, we feel the statement is overly verbose: We only need columns of the template_attribute table, but the values arguments need to maintain a fully qualified path starting from the original Product model. See the code below:
# Getting the `id` columns from multiple-orders of related models:
attribute_set = template. values(
"id", # the base model, Product
"template__id", # the related model, Template
"template__templateattribute__id" # the second related model, TemplateAttribute
)
So, we wonder if there is a way to refer to the columns directly from the containing model, e.g. templateattribute__id, or even better id, instead of template__templateattribute__id.
We are new to the Django ORM and appreciate any hints or suggestions.
Actions:
template = Product.active_objects.filter(id='xxx').select_related('template')
attribute_set = template. values("id", "template__id", "template__templateattribute__id")
for i, attr in enumerate(attribute_set):
print("{:03}: {}".format(i, attr))
# Output:
# 000: {'id': xxx, 'template__id': xxxx, 'template__templateattribute__id': xxxxx}
# 001: {'id': xxx, 'template__id': xxxx, 'template__templateattribute__id': xxxxx}
# 002: {'id': xxx, 'template__id': xxxx, 'template__templateattribute__id': xxxxx}
# ...
The models:
# Simplified toy example
class Product(models.Model):
product_template = models.ForeignKey(Template)
sku = models.CharField(max_length=100)
...
class Template(models.Model):
base_sku = models.CharField(max_length=100)
...
class TemplateAttribute(models.Model):
product_template = models.ForeignKey(Template)
attribute = models.ForeignKey(eav_models.Attribute)
...
# From the open-source Django EAV library
# imported as `eav_models`
#
class Attribute(models.Model):
name = models.CharField(_(u"name"), max_length=100,
help_text=_(u"User-friendly attribute name"))
...
slug = EavSlugField(_(u"slug"), max_length=50, db_index=True,
help_text=_(u"Short unique attribute label"))
...
Perhaps using a Manager with annotations or aliases could help?
You could try to add more magic by trying to dynamically add an annotation for each key during the manager's construction, but really at that point you are writing code that should have been in the EAV itself.
I would warn you, having attribute names and their values in a table instead of just model fields (and columns in the DB) will be an uphill battle, and already you are finding areas where your library isn't handling things for you.
from django.db import models
from django.db.models import F
class ProductManager(models.Manager):
def get_queryset(self):
qs = super().get_queryset() \
.annotate(
my_attribute_key=F('template__templateattribute__id')
)
return qs
In addition, we also figured out a workaround by defining a base path:
base = "template__templateattribute"
So, instead of
attribute_set = template. Values("template__templateattribute__id")
, we can do the following:
attribute_set = template. Values(base+"__id")
This is just an informal workaround, though. The better way is still to use a Manager class.
I have made a field facility_id in Django models that should concatenate a specific string "ACCTS-" on the left with each record's id on the right,
My model class is below:
class Facility(models.Model):
...
id = models.BigAutoField(primary_key=True)
facility_id = models.CharField(max_length=50, default=print(f'{"ACCTS-"}{id}'), editable=False)
...
I want to the facility_id field to be storing special and readable human friendly facility_id's of the form: ACCTS-1, ACCTS-2, ACCTS-3, ... corresponding to each individual id.
The migrations didn't throw any errors, however When I try to create the records for this table in the Django Admin, am getting an IntegrityError of:
IntegrityError at /admin/ACCTS_CLYCAS/facility/add/
NOT NULL constraint failed: ACCTS_CLYCAS_facility.facility_id
How do I fix this problem, or what could be the easiest way to implement my problem.
The migrations didn't throw any errors, however When I try to create the records for this table in the Django Admin
That makes sense, since you have set the default=None. Indeed, print(…) returns None and only prints the value to the standard output channel (stdout). It will thus not prepend the value of the id with ACCTS.
If the facility_ids are all just the id prefixed with ACCTS-, you can work with a #property instead:
class Facility(models.Model):
id = models.BigAutoField(primary_key=True)
#property
def facility_id(self):
return f'ACCTS-{self.id}'
You can also try using a post save signal.
Add blank = True to facility_id and then use a post save signal to update the value of facility_id.
You can watch this tutorial on how to use Django Signals
I've successfully used Graphene-Django to successfully build several GraphQL calls. In all of those cases I populated, in whole or in part, a Django model and then returned the records I populated.
Now I have a situation where I'd like to return some data that I don't wish to store in the Django model. Is this possible to do with Graphene?
Robert
Robert_LY answered his own question perfectly in the comments, I'd just like to expand his solution.
My database-less model WordForm is generated automatically, without storing it in a database. I define it as a Django model as follows:
from django.db import models
class WordForm(models.Model):
value = models.CharField(max_length=100)
attributes = models.CharField(max_length=100)
In the schema I define the node and query like this:
class WordFormNode(DjangoObjectType):
class Meta:
model = WordForm
interfaces = (relay.Node, )
class Query(AbstractType):
word_forms = List(WordFormNode,query=String(),some_id=String())
def resolve_word_forms(self, args, context, info):
query= args['query']
some_id = from_global_id(args['some_id'])[1]
word_forms = []
# some logic to make WordForm objects with
# WordForm(value=value,attributes=attributes),
# then append them to list word_forms
return word_forms
You can pass as many arguments as you like to the List and access them in resolve_word_forms.
When you map your Django model to a GraphQL, it create a new model with GraphQL object types from the introspection of the Django model..
And nothing prevent you to combine this model with with plain GraphQL objects types, or mapped from an other third party persistence model
tl;dr: is it possible, with endpoints-proto-datastore, to receive a list with objects from a POST and insert it in the db?
Following the samples, when building my API i didn't got how could i let the users POST a list of objects so that i could be more efficient about putting a bunch of data in the db using ndb.put_multi, for example.
From this comment here at endpoints_proto_datastore.ndb.model i imagine that it is not possible with how it is designed. Am i right or i am missing something?
Extending the sample provided by endpoints achieved the desired with:
class Greeting(messages.Message):
message = messages.StringField(1)
class GreetingCollection(messages.Message):
items = messages.MessageField(Greeting, 1, repeated=True)
# then inside the endpoints.api class
#endpoints.method(GreetingCollection, GreetingCollection,
path='hellogretting', http_method='POST',
name='greetings.postGreeting')
def greetings_post(self, request):
result = [item for item in request.items]
return GreetingCollection(items=result)
-- edit --
See the docs about POSTing into the datastore, your only issue is that your models aren't EndpointsModels. Instead define a datastore model for both your Greeting and GreetingCollection:
from endpoints_proto_datastore.ndb import EndpointsModel
class Greeting(EndpointsModel):
message = ndb.StringProperty()
class GreetingCollection(EndpointsModel):
items = ndb.StructuredProperty(Greeting, repeated=True)
Once you've done this, you can use
class MyApi(remote.Service):
# ...
#GreetingCollection.method(path='hellogretting', http_method='POST',
name='greetings.postGreeting')
def greetings_post(self, my_collection):
ndb.put_multi(my_collection.items)
return my_collection
I'm doing a simple program about customers, products and drafts.
Since they are referenced to each other in some way, when I delete one entity of a kind, another entity of another kind might give an error.
Here's what I have:
-customer.py
class Customer(db.Model):
"""Defines the Customer entity or model."""
c_name = db.StringProperty(required=True)
c_address = db.StringProperty()
c_email = db.StringProperty() ...
-draft.py
class Draft(db.Model):
"""Defines the draft entity or model."""
d_customer = db.ReferenceProperty( customer.Customer,
collection_name='draft_set')
d_address = db.StringProperty()
d_country = db.StringProperty() ...
Ok, now what I want to do is check if a customer has any Draft referencing to him, before deleting him.
This is the code I'm using:
def deleteCustomer(self, customer_key):
'''Deletes an existing Customer'''
# Get the customer by its key
customer = Customer.get(customer_key)
if customer.draft_set: # (or customer.draft_set.count > 0...)
customer.delete()
else:
do_something_else()
And now, it comes the problem.
If I have a draft previously created with the selected customer on it, there's no problem at all, and it does what has to do. But if I haven't created any draft that references to that customer, when trying to delete him, it will show this error:
AttributeError: 'Customer' object has no attribute 'draft_set'
What am I doing wrong? Is it needed to always create a Draft including a Customer for him to have the collection_name property "available"?
EDIT: I found out what the error was.
Since I have both classes in different .py files, it seems that GAE loads the entities into the datastore at the same moment as it "goes through" the file that contains that model.
Therefore, if I'm executing the program, and never use or import that file, the datastore is not updated until then.
Now what I'm doing is:
from draft.py import Draft
inside de "deleteCustomer()" function and it's finally working fine, but I get a horrible "warning not used" because of so.
Is there any other way I can fix this?
The collection_name property a query, so it should always be available.
What you may be missing is the reference_class parameter (check the ReferenceProperty docs)
class Draft(db.Model):
"""Defines the draft entity or model."""
d_customer = db.ReferenceProperty(reference_class=customer.Customer, collection_name='draft_set')
The following should work:
if customer.draft_set.count():
customer.delete()
note that customer.draft_set will always return true, as it is the generated Query object, so you MUST use the count()
There were two possible solutions:
Ugly, bad one: as described in my edited question.
Best practice: put all the models together inside one file (e.g. models.py) that looks like this:
class Customer(db.Model):
"""Defines the Customer entity or model."""
c_name = db.StringProperty(required=True)
c_address = db.StringProperty()
c_email = db.StringProperty() ...
class Draft(db.Model):
"""Defines the draft entity or model."""
d_customer = db.ReferenceProperty( customer.Customer,
collection_name='draft_set')
d_address = db.StringProperty()
d_country = db.StringProperty() ...
Easy!