I have three tables for different payment types, and want to create a table that holds payments made using all three. I'm not sure if I'm going about this the right way, but I was going to create a foreign key column in the table for each of the three, and the write a constraint such that exactly one of those columns has to be not null.
Is this the right way to go about this?
How do you go about writing this constraint?
Is there any way to do this from within SQLAlchemy on sqlite? (code for declarative classes would be much appreciated)
Have a single foreign key column, and a separate type column so you know which table to look in.
Ok I've got it - Is this the best way to do it? - I created a generic id field table as such:
class PaymentDetails(Base):
__tablename__ = 'payment_details'
id = Column(Integer, primary_key=True)
type = Column(PaymentType.db_type(), nullable=False)
where PaymentType uses the declarative enum recipe and and then subclassed this for the various payment methods:
#concrete
#do_unique_index('paypal_unique_details', 'env', 'main_email', 'sub_email')
class Paypal(Base):
__tablename__ = 'paypal_details'
id = Column(ForeignKey('payment_details.id'), primary_key=True)
# The rest of the implementation
#
#concrete
#do_unique_index('credit_card_unique_details', 'env', 'card_number')
class CreditCard(Base):
__tablename__ = 'card_details'
id = Column(ForeignKey('payment_details.id'), primary_key=True)
# The rest of the implementation
#
#concrete
#do_unique_index('time_code_unique_details', 'env', 'code')
class TimeCodes(Base):
__tablename__ = 'code_details'
id = Column(ForeignKey('payment_details.id'), primary_key=True)
# The rest of the implementation
#
(Where concrete and do_unique_index set the relevant __mapper_args__ and __table_args__). I then set the description field of the PaymentType enum values to be each of these classes, so that to look up a payment I can query for a PaymentDetails object, then get an id and a type from that, say id and Paypal, to perform a second query for the Paypal with that id.
My code for adding sets of details is fairly simple in that in a single transaction, it adds the next logical id to the PaymentDetails table with the type of the payment details we are trying to create, and then adds an entry to that table with the details I want to enter. I can then add methods to these ORM classes to handle the different ways that we would handle buying, selling, and refunding for each method, such that they can be treated as identical.
You then need to switch on FK constraints as van mentioned - I did so by adding the FK listener to the helper class I use for DB access.
Related
I have a table UserStoreName,
Columns are :
int Id
string UserNameId (as a FK of the table AspNetUsers (Column Id))
sring StoreName
I have a page AddStore, a very simple page where user just enter the store name into the StoreName Field.
I already know the UserNameId, i'm taking it from the User.
So when user populate the storeName field and click submit i just need to add a record to the table UserStoreName.
sounds easy.
when i click submit the AddStore function from the controller is giving me ModelState.IsValid = false.
reason for that is cause userNameId is a required field.
i want to populate that field in the AddStore
function but when we get there the modelState is already invalid because of a required field in userStoreNameId enter code here
Here is the AddStore in case it will help :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddStore(UserStoreName userStoreName)
{
userStoreName.UserNameId =
(_unitOfWork.ApplicationUser.GetAll().Where(q => q.UserName == User.Identity.Name).Select(q => q.Id)).FirstOrDefault();
userStoreName.UserName = User.Identity.Name;
userStoreName.IsAdminStore = false;
if (ModelState.IsValid)
{
_unitOfWork.UserStoreName.Add(userStoreName);
_unitOfWork.Save();
return RedirectToAction(nameof(Index));
}
return View(userStoreName);
}
Any idea what am i doing wrong? new to asp.net core mvc, its my first project.
Thanks :)
Thank you
If the UserNameId field is required, it must be supplied to pass model validation.
There are two ways around this. First, you could create a View Model, with just the fields you plan on actually submitting, and use it in place of the userStoreName variable. Then in the controller action, you can just instantiate a new UserStoreName object, and fill out the fields.
Alternatively, you could pass the UserNameId variable to the view, and populate the model client side using a hidden field, so it passes validation when returned to the controller. Hidden fields can potentially have their values edited client-side, however, so it may be worth checking the value again server side, especially if there are any security implications.
Foreign keys can be nullable so just make sure the UserNameId field is not marked with the "[Required]" Data Annotation in your model.
You'll also need to make sure that the column is nullable on the UserStoreName table to match the model otherwise it'll cause problems if your model is different from its underlying table.
Just a small suggestion also, I wouldn't foreign key on strings, I would change your model foreign key to an int, and make sure that the column in the table it's related to is also an int. It's a lot safer to do so, especially if you're dealing with IDENTITY columns.
If there is anything wrong with the reference, an exception will throw when the code tries to save your change, usually because the value it has in the FK reference cannot be found in the related table.
I've been trying to update an Entity using the following code:
var db = new MyContext();
var data = db.Tableau.Find(Id);
if (data != null)
{
data.Name = model.Name;
data.EmbedCode = model.EmbedCode;
db.SaveChanges();
}
The problem is that my Tableaus table has a Parent field (FK not null to a DataTree table). Sometimes when I save the changes to this edited record, I get an error saying that "The Parent field is required". But I am not editing the Parent field. The parent field should be intact and existent, since I am only altering the Name and EmbedCode fields.
How to proceed? Thanks in advance.
That is because you are allowing null values in ParentId column in your Tableaus table, but in your Tableau entity you have ParentId as non-nullable property( which it means the relationship is required), and when you load a Tableau instance from your DB, EF expects that you set that property too. Try changing that property to nullable:
public int? ParentId {get;set;}
If you configure your relationship using Fluent Api it would be:
modelBuilder.Entity<Tableau>()
.HasOptional(t=>t.Parent)
.WithMany(dt=>dt.Tablous)// if you don't have a collection nav. property in your DataTree entity, you can call this method without parameter
.HasForeignKey(t=>t.ParentId);
Update 1
If you want ParentId property as Required in your Tableau entity, you need to make sure that you have a valid value in that column in your DB per each row. With a "valid value" I mean it should be different of the default value and it should exist as PK in your DataTree table.
Update 2:
One way to load a related entity as part of your query is using Include extension method:
var data = db.Tableau.Include(t=>t.Parent).FirstOrDefault(t=>t.Id==Id);
This question completely follows on from a related question I had asked (and was answered) here: Error when trying to retrieve a single entity
As I understand, to retrieve a single entity from the datastore using a property other than helper methods already provided (e.g. 'id') requires turning a simple data property into an EndpointsAliasProperty? If yes, how would I go about doing that? Or is it that we can only use 'id' (helper methods provided by EndpointsModel) and we cannot use any of the properties that we define (in this case 'title')?
The distinction between the custom EndpointsAliasPropertys and one of the data properties you defined is how they are used. They are all used to create a protorpc message, and that message is then converted into an EndpointsModel with your custom data in it. THIS is where the magic happens.
Breaking it down into steps:
1. You specify your data
from google.appengine.ext import ndb
from endpoints_proto_datastore.ndb import EndpointsModel
class MyModel(EndpointsModel):
my_attr = ndb.StringProperty()
2. You pick your fields for your method
class MyApi(...):
#MyModel.method(request_fields=('id', 'my_attr'), ...)
def my_method(self, my_model_entity):
...
3. A protorpc message class is defined from your fields
>>> request_message_class = MyModel.ProtoModel(fields=('id', 'my_attr'))
>>> request_message_class
<class '.MyModelProto_id_my_attr'>
>>> for field in request_message_class.all_fields():
... print field.name, ':', field.variant
...
id : INT64
my_attr : STRING
This happens every time a request is handled by a method decorated with #MyModel.method.
4. A request comes in your application and a message is created
Using the protorpc message class, a message instance is parsed from the JSON which gets passed along to your Endpoints SPI (which is created by endpoints.api_server).
When the request comes in to your protorpc.remote.Service it is decoded:
>>> from protorpc import remote
>>> protocols = remote.Protocols.get_default()
>>> json_protocol = protocols.lookup_by_content_type('application/json')
>>> request_message = json_protocol.decode_message(
... request_message_class,
... '{"id": 123, "my_attr": "some-string"}'
... )
>>> request_message
<MyModelProto_id_my_attr
id: 123
my_attr: u'some-string'>
5. The protorpc message is cast into a datastore model
entity = MyModel.FromMessage(request_message)
THIS is the step you really care about. The FromMessage class method (also provided as part of EndpointsModel) loops through all the fields
for field in sorted(request_message_class.all_fields(),
key=lambda field: field.number):
and for each field with a value set, turns the value into something to be added to the entity and separates based on whether the property is an EndpointsAliasProperty or not:
if isinstance(value_property, EndpointsAliasProperty):
alias_args.append((local_name, to_add))
else:
entity_kwargs[local_name] = to_add
After completing this loop, we have an ordered list alias_args of all key, value pairs and a dictionary entity_kwargs of the data attributes parsed from the message.
Using these, first a simple entity is created
entity = MyModel(**entity_kwargs)
and then each of the alias property values are set in order:
for name, value in alias_args:
setattr(entity, name, value)
The extended behavior happens in setattr(entity, name, value). Since EndpointsAliasProperty is a subclass of property, it is a descriptor and it has a setter which can perform some custom behavior beyond simply setting a value.
For example, the id property is defined with:
#EndpointsAliasProperty(setter=IdSet, property_type=messages.IntegerField)
def id(self):
and the setter performs operations beyond simply setting data:
def IdSet(self, value):
self.UpdateFromKey(ndb.Key(self.__class__, value))
This particular method attempts to retrieve the entity stored in the datastore using the id and patch in any values from the datastore that were not included in the entity parsed from the request.
If you wanted to do this for a field like my_attr, you would need to construct a custom query which could retrieve the item with that unique my_attr value (or fail if not exactly one such entity exists).
This is problematic and you'd be better off using a unique field like the key or ID used to store the entity in the datastore.
The keys with ancestors sample gives a great example of creating your own custom properties.
If you REALLY insist on using my_attr to retrieve an entity, you could do so using a different property name (since my_attr is already used for the data property) such as fromMyAttr:
class MyModel(EndpointsModel):
def MyAttrSet(self, value):
...
#EndpointsAliasProperty(setter=MyAttrSet)
def fromMyAttr(self):
...
Here, the MyAttrSet instance method would form the query:
def MyAttrSet(self, value):
query = MyModel.query(MyModel.my_attr == value)
results = query.fetch(2)
reject results that aren't unique for my_attr:
if len(results) == 0:
raise endpoints.NotFoundException('Not found.')
if len(results) == 2:
raise endpoints.BadRequestException('Colliding results.')
and copy over the values for the already stored entity if we do find a unique one:
matching_entity = results[0]
self._CopyFromEntity(matching_entity)
self._from_datastore = True
I'm writing a custom validator for a specific constrain group (not Default), but the runtime gives me the below error.
I'm just curious why they need the default values to be empty. Appreciate if you can share your opinion. Thanks :)
xxx.model.validation.CustomValidation contains Constraint annotation, but the groups parameter default value is not the empty array.
StackTrace: org.hibernate.validator.metadata.ConstraintHelper.assertGroupsParameterExists(ConstraintHelper.java:335)
org.hibernate.validator.metadata.ConstraintHelper.isConstraintAnnotation(ConstraintHelper.java:282)
I can't figure out in which scenario it can be useful to bind a constraint to a specific group.
A group is used for partial validation (and sequence validation). If you have 10 fields in a class you can mark 8 of them with A and 2 with B. Then you can decide to validate only the fields in the A group or in the B group. Conversely you want that #MyConstraint belongs to a specific group named C. It makes no sense. A group is more or less a name used to distinguish some fields from others in the same class. It has no absolute meaning. Groups are useful in validation not in constraint definition, they are related to fields not to constraints.
Furthermore if you hide the group name in the constraint definition you may run into errors because you can think that the fields are validated all togheter.
#Email
private String mail;
#Password
private String pass;
#VAT
private String vatCode;
Are you able to see if there is a partial validation?
EDIT
In relation to the second comment:
Suppose you have a class with 5 fields with no constraint at all. Three of them are integers. If you want to validate the sum of these three fields you have to create a class-level constraint as you suggest. In this way your custom annotation is applied to the class not to fields so, how can you define groups on fields?
Instead, you may use something like this:
#Sum(min = 250, fields = {"length", "width", "height"})
public class MyClass {
private String type;
private String code;
private int length;
private int width;
private int height;
...
}
I have a single page which collects related data into a series of input fields.
When the user clicks submit I want to insert the data into two different database tables in one go. ie I want to get the primary key from the first insert, and then use it for the second insert operation.
I am hoping to do this all in one go, but I am not sure of the best way to do this with the Models/Entities in MVC.
Are you using LINQ or some other ORM? Typically these will support the ability to add a related entity and handle the foreign key relationships automatically. Typically, what I would do with LINQtoSQL is something like:
var modelA = new ModelA();
var modelB = new ModelB();
UpdateModel( modelA, new string[] { "aProp1", "aProp2", ... } );
UpdateModel( modelB, new string[] { "bProp1", "bProp2", ... } );
using (var context = new DBContext())
{
modelA.ModelB = modelB;
context.ModelA.InsertOnSubmit( modelA );
context.SubmitChanges();
}
This will automatically handle the insertion of modelA and modelB and make sure that the primary key for modelA is set properly in the foreign key for modelB.
If you are doing it by hand, you may have to do it in three steps inside a transaction, first inserting modelA, getting the key from that insert and updating modelB, then inserting modelB with a's data.
EDIT: I've shown this using UpdateModel but you could also do it with model binding with the parameters to the method. This can be a little tricky with multiple models, requiring you have have separate prefixes and use the bind attribute to specify which form parameters go with which model.