SQLModel/SQLAlchemy updating parent is causing new children - sqlalchemy

I am using SQLModel to maintain a Region, which contain multiple areas.
These areas have in turn events through a many to many link table.
Creating my region with areas work fine, but if I update information in the region it duplicates all areas, all referencing the main region.
I am looking for a simplish way to make sure that if the region is the same, areas are the same and events on the areas are the same, the existing objects (and their ID's) are used.
This is my model:
class Region(SQLModel, table=True):
region_id: int = Field(primary_key=True)
name: str
description: Optional[str]
sourceAbbreviation: str
chapter: str
areas: Optional[List["Area"]] = Relationship(back_populates="region")
class AreaEvent(SQLModel, table=True):
area_id: int = Field(foreign_key="area.area_id", primary_key=True)
event_id: int = Field(foreign_key="event.event_id", primary_key=True)
class Area(SQLModel, table=True):
area_id: int = Field(primary_key=True)
name: str
description: Optional[str]
region_id: int = Field(foreign_key="region.region_id")
region: Optional["Region"] = Relationship(back_populates="areas")
events: Optional[List['Event']] = Relationship(back_populates="areas", link_model=AreaEvent)
class Event(SQLModel, table=True):
event_id: int = Field(primary_key=True)
name: str
areas: Optional[List[Area]] = Relationship(back_populates="events", link_model=AreaEvent)
And this is my retrieval method - nb: the get_adventureparts method returns a region with child areas.
#router.post("/region/{region_name}", response_model=RegionResponse)
async def add_region(
region_name: str,
sourceAbbreviation: str,
chapter: str,
session: Session = Depends(get_session)
):
db_region = session.query(Region).options(selectinload(Region.areas)).filter(Region.name == region_name.lower(),
Region.sourceAbbreviation == sourceAbbreviation.lower()).one_or_none()
if db_region is not None:
new_region = get_adventureparts(db_region)
for key, value in vars(new_region).items():
if key != "_sa_instance_state" and value is not None:
setattr(db_region, key, value)
session.commit()
region = session.query(Region).get(db_region.region_id)
else:
new_region = get_adventureparts(
Region(name=region_name.lower(), description="", sourceAbbreviation=sourceAbbreviation.lower(),
chapter=chapter))
session.add(new_region)
session.commit()
region = session.query(Region).get(new_region.region_id)
return RegionResponse.from_orm(region)

Related

SQLAlchemy Class is not mapped

Im trying to update a user in FastAPI but i'm currently getting the error - Class 'api.schemas.instructor.Instructor' is not mapped
I've tried a couple of things to fix this like changing the schema and model but the results are either the error above or an error stating that the user already exists in the DB, is there anyway this can be resolved via the schema?
Heres my code;
Instructor schema
class Instructor(BaseModel):
id: str
full_name: str
phone_number: int
email: str
is_active: bool
is_superuser: bool
sex: str
car_type: str
area_covered: str
driving_school_name: str
driving_school_description: Optional[str] = None
adi_license: str
profile_image_one: Optional[str] = None
profile_image_two: Optional[str] = None
profile_image_three: Optional[str] = None
class Config:
orm_mode = True
Patch endpoint
#instructors.patch("/{user_id}")
def update(user_id: str, user: InstructorUpdate = Depends(InstructorUpdate.as_form), db: Session = Depends(d.get_db), profile_images: Optional[List[UploadFile]] = File(None)):
current_user = Instructor(**jsonable_encoder(crd.get_user(db, user_id)))
# current_user = jsonable_encoder(crd.get_user(db, user_id))
if current_user:
updated_user = crd.update_user(db, current_user, user, image_keys=image_keys)
return updated_user
else:
raise HTTPException(status_code=400, detail="User not found")
Update user function
def update_user(self, db: Session, db_obj: ModelType, usup: Any, image_keys: Optional[List[str]] = None):
obj_data = db_obj
if isinstance(usup, dict):
update_data = usup
else:
update_data = usup.dict(exclude_unset=True)
for field in obj_data:
if field in update_data:
if update_data[field] is not None:
setattr(db_obj, field, update_data[field])
# Todo - Error here "Class 'api.schemas.instructor.Instructor' is not mapped", need to fix this
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
Any help would be appreciated.

json data parsing using retrofit and rxjava2 data display in Textview and TableLayout

JSON Data Parsing Using Retofit2 and Rxjava2. This Data get In ArrayList successfully. its ArrayList Size is Nine but its display only two Record in Table. After Two Record its Kotlin.NullPointerException.
JSON Data:
{"success":1,"salesGst":[{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"1","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"2778.75","TaxTotal":"2778.75","InvoiceType":"Retail Invoice","CGSTTotal":"0.0","PartyGST":"CDE","SGSTTotal":"0.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"1","ChallanDate":"2019-03-13 00:00:00","ChallanAmount":"2203.0","TaxTotal":"2118.5","InvoiceType":"Tax Invoice","CGSTTotal":"52.96","PartyGST":"CDE","SGSTTotal":"52.96","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"VIKAS","ChallanNo":"2","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"6975.0","TaxTotal":"6975.0","InvoiceType":"Retail Invoice","CGSTTotal":"0.0","PartyGST":null,"SGSTTotal":"0.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES MH","ChallanNo":"2","ChallanDate":"2019-03-13 00:00:00","ChallanAmount":"420.0","TaxTotal":"403.75","InvoiceType":"Tax Invoice","CGSTTotal":"0.0","PartyGST":"ABC","SGSTTotal":"0.0","IGSTTotal":"20.19"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"3","ChallanDate":"2019-03-14 00:00:00","ChallanAmount":"4788.0","TaxTotal":"4560.0","InvoiceType":"Tax Invoice","CGSTTotal":"114.0","PartyGST":"CDE","SGSTTotal":"114.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"4","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"241.9","TaxTotal":"230.38","InvoiceType":"Tax Invoice","CGSTTotal":"5.76","PartyGST":"CDE","SGSTTotal":"5.76","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"5","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"5563.68","TaxTotal":"5101.5","InvoiceType":"Tax Invoice","CGSTTotal":"231.28","PartyGST":"CDE","SGSTTotal":"231.28","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"6","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"13238.0","TaxTotal":"12459.25","InvoiceType":"Tax Invoice","CGSTTotal":"389.29","PartyGST":"CDE","SGSTTotal":"389.29","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES MH","ChallanNo":"7","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"2074.0","TaxTotal":"1975.0","InvoiceType":"Tax Invoice","CGSTTotal":"0.0","PartyGST":"ABC","SGSTTotal":"0.0","IGSTTotal":"98.75"}]}
Please Guide Me,After Getting How to Show in TableLayout.
In ArrayList Nine Record but in Table show only Two Record another seven record is not display. in third record taxtotal give kotlin.nullpointerException. what missing?
private fun displaySalesGSTData(salesGSt : List<SalesGST>) {
salesGST = SalesGST()
tvSalesCompanyName.setText(salesGSt.get(1).Cmp_Name)
tvGSTIN.setText(salesGSt.get(1).GSTIN)
val rowHeader = TableRow(this#Sales)
rowHeader.setBackgroundColor(Color.parseColor("#c0c0c0"))
rowHeader.setLayoutParams(TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT))
val headerText = arrayOf<String>("Sr.No.", "Invoice Type", "Bill No.", "Bill Date", "Firm Name", "GST NO","TAX Total","CGST","SGST","IGST","Net Amount")
for (c in headerText)
{
val tv = TextView(this#Sales)
tv.setLayoutParams(TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT))
tv.setGravity(Gravity.CENTER)
// tv.setBackgroundResource(R.drawable.table_header)
tv.setTextColor(Color.parseColor("#3F51B5"))
tv.setTextSize(18F)
tv.setPadding(5, 5, 5, 5)
tv.setText(c)
rowHeader.addView(tv)
}
tableMarks.addView(rowHeader)
for (j in 0 until salesGSt.size)
{
/*val jsonObject1 = jsonArray.getJSONObject(j)
val date = jsonObject1.getString("ExamDate")
val inputFormatter1 = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date1 = inputFormatter1.parse(date)
val outputFormatter1 = SimpleDateFormat("dd-MMM-yyyy")
ExamDate = outputFormatter1.format(date1)*/
/* String replaceDate = date.replace("/Date(", "").replace(")/", "");
Long getDate = Long.valueOf(replaceDate);
ExamDate = dateFormat.format(getDate);*/
/*Subject = jsonObject1.getString("subject")
ExamName = jsonObject1.getString("ExamName")
TotalMark = jsonObject1.getLong("TotalMarks")
PassingMark = jsonObject1.getLong("PassingMarks")
Mark = jsonObject1.getLong("Marks")*/
var fName : String = salesGSt.get(j).FirmName!!
var invoice : String = salesGSt.get(j).InvoiceType!!
var bill_no : String = salesGSt.get(j).ChallanNo!!
var bill_date : String = salesGSt.get(j).ChallanDate!!
var gst_no : String = salesGSt.get(j).PartyGST!!
var tax_total : Double = salesGSt.get(j).TaxTotal!!.toDouble()
var cgst : String = salesGSt.get(j).CGSTTotal!!
var igst : String = salesGSt.get(j).IGSTTotal!!
var sgst : String = salesGSt.get(j).SGSTTotal!!
var net_amount : String = salesGSt.get(j).ChallanAmount!!
var sr : Int = j + 1
// dara rows
val row = TableRow(this#Sales)
row.setLayoutParams(TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT))
val colText = arrayOf<String>(sr.toString(),(invoice), bill_no, bill_date, fName, gst_no, tax_total.toString(),cgst,sgst,igst,net_amount)
for (text in colText)
{
val tv = TextView(this#Sales)
tv.setLayoutParams(TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT))
tv.setGravity(Gravity.CENTER)
// tv.setBackgroundResource(R.drawable.table_shape)
tv.setTextSize(18F)
tv.setTextColor(Color.parseColor("#3F51B5"))
tv.setPadding(5, 5, 5, 5)
tv.setText(text)
row.addView(tv)
}
tableMarks.addView(row)
}
}
The 3rd item, salesGst[2], is "PartyGST": null. Your json deserializer library won't handle non-null fields as it's written in Java. I assume you have a data class where PartGST is defined as non-null, yet the deserializer will still parse it as null. Therefore, when you access PartyGST then you will receive a NullPointerException because Kotlin is expecting it to be non-null. This is a good article, which explains in more detail:
I've trusted you! You promised no null pointer exceptions!
A solution to this would be to have two models. The DTO (for JSON response) where all fields are optional and your internal model (used by your app), where you define which fields you want to be optional. Then you can have a mapper to handle the case where a field is null in the DTO, but non-null in your internal model:
// Models for your API response
data class SalesGstsDTO(val gsts: List<GstDTO>)
data class GstDTO(val name: String?, val surname: String?)
// Internal models used by your app
data class SalesGsts(val gsts: List<Gst>)
data class Gst(val name: String, val surname: String?)
class SalesGstDTOMapper {
fun mapToSalesGsts(salesGstsDTO: SalesGstsDTO): SalesGsts {
val gsts = mutableListOf<Gst>()
salesGstsDTO.gsts.map {
val name = it.name ?: return#map // Skips this item. You could handle this how you wish
val surname = it.surname
val gst = Gst(name, surname)
gsts.add(gst)
}
return SalesGsts(gsts)
}
}
This also allows you to decouple your app from the JSON response.

Create Object from JSON with id predefined in Grails

I have a problem! need to get a json object that already with an id already set that is not yet saved. but when trying to create the object id is null. And it is only set when call the save method generating different id's.
ex:
objetoJson = {id: "123413215123", name: "Leonan Teixeira", address: {id: "12345", city: "Victory", state: "Bahia"}}
def person = new Person (objetoJson)
end state of the object:
Person.id = null
Person.Address = null
but I need the id are stored.
the same happens if I do
person.properties = objetoJson
My classes are mapped with id 'uuid'
String id;
static mapping = {
id generator: 'uuid'
}
Solution
add a bindable:true to the constraints
class MyDomain {
static constraints = {
// allow binding of "id" attribute (e.g. in constructor or url parameters)
id bindable: true
}
}
http://www.redcube.de/assigning-id-domain-objects-grails-via-constructor/
Try this:
def jsonSlurper = new JsonSlurper()
def dataPerson = jsonSlurper.parseText('{ "id":"123456789", "nome": "Leonardo Meurer", "age":"29" }')
def person = new Person (dataPerson)

Play-Slick plugin, throws abstraction error in case of DateTime variables, data types

I have been using slick 2 as the dbms for my play app, though using play-slick plugin rather then slick independently, and added the tototoshi plugins as well, dependencies are:
"joda-time" % "joda-time" % "2.4"
"org.joda" % "joda-convert" % "1.6"
"com.github.tototoshi" %% "slick-joda-mapper" % "1.2.0"
"com.typesafe.play" %% "play-slick" % "0.6.1"
"com.typesafe.slick" %% "slick" % "2.0.3"
And the case class and projection are :
//Slick imports used
import play.api.db.slick.Config.driver.simple._
import play.api.db.slick.DB
import scala.slick.lifted.ProvenShape
import com.github.tototoshi.slick.MySQLJodaSupport._
case class Design(
var id: Int,
var imageName: String,
var title: String,
var creatorID: Int,
var flagged: Boolean,
var modifiedTimestamp: Instant,
var createdTimestamp: Instant) {
def this() = this(0, "", "", 0, false, DateTime.now.toInstant, DateTime.now.toInstant)
}
class DesignProjection(tag: Tag) extends Table[Design](tag, "designs_47") {
def id: Column[Int] = column[Int]("id", O.PrimaryKey, O.AutoInc)
def imageName: Column[String] = column[String]("des_image_name")
def title: Column[String] = column[String]("des_title")
def creatorID: Column[Int] = column[Int]("des_creator_id")
def flagged: Column[Boolean] = column[Boolean]("des_flagged_link")
def modifiedTimestamp: Column[Instant] = column[Instant]("tt_modified_timestamp")
def createdTimestamp: Column[Instant] = column[Instant]("tt_tweeted_timestamp")
def * : ProvenShape[Design] = (id, imageName, title, creatorID, flagged, modifiedTimestamp, createdTimestamp) <> (
((Design.apply _): (Int, String, String, Int, Boolean, Instant, Instant) => Design).tupled,
Design.unapply)
}
And when I try to list all rows using method :
def list: List[Design] = {
println("Start Listing")
val result = DB.withSession { implicit session =>
val res = designProjection.list <-error here
res
}
convertListResultSet(result)
}
I get [RuntimeException: java.lang.AbstractMethodError], well I am more then sure its cause of the DataTime class, but I really don't know whats going wrong. I have used the tototoshi plugins as well. All other cases where DateTime are considered, they are working fine.
Any help or pointers are really welcome. Thank you
It is a known issue.
https://github.com/tototoshi/slick-joda-mapper/issues/19
Could you try slick-joda-mapper 1.1.0 ?

dict–set proxy on self-referential many-to-many SQLAlchemy association

In my model, I have a base object class A which holds a set of attributes. Each object of A can be connected to any other object of A through a many-to-many association object Context. This Context class holds a key for every connection.
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
# other attributes
value = Column(Integer)
def __init__(self, value):
self.value = value
class Context(Base):
__tablename__ = "context"
holder_id = Column(Integer, ForeignKey("a.id"), nullable=False, primary_key=True)
attachment_id = Column(Integer, ForeignKey("a.id"), nullable=False, primary_key=True)
key = Column(String, primary_key=True)
holder = relationship(A,
primaryjoin=lambda: Context.holder_id==A.id)
attachment = relationship(A,
primaryjoin=lambda: Context.attachment_id==A.id)
The Context class therefore stores 3-tuples of the form ‘Holder object a1 holds attachment a2 with key k1’.
I now want to have a smart proxy collection on A which groups this relationship on the Context.key such that I can use it as follows:
a1 = A(1)
a1.context["key_1"] = set([A(2)])
a1.context["key_2"] = set([A(3), A(4), A(5)])
a1.context["key_1"].add(A(10))
a100 = A(100)
a100.context = {
"key_1": set([A(101)])
}
How do I have to define context?
I know there is an example for modelling a dict–set proxy in the SQLAlchemy documentation but somehow I am not able to get it to work in a self-referential situation.
Unless I'm blanking a bit (which is possible...gin...) this can't be done with association proxy directly on the relationship because a1.context would need to be a collection where each element has a unique key, then the collection can be a dictionary - but there is no such collection here, as a1 can have many Context objects with the same key. Assoc prox's simple way of reducing a collection of objects to a collection of an attribute on each member object doesn't apply to this.
So if you really want this, and your structure can't change, just do what association proxy does, just in a hardcoded way, which is, build a proxying collection ! actually two, I think. Not too big a deal, just need to turn the crank....quite a bit, make sure you add tests for every manipulation here:
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
import itertools
Base= declarative_base()
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
# other attributes
value = Column(Integer)
def __init__(self, value):
self.value = value
#property
def context(self):
return HolderBySetDict(self)
#context.setter
def context(self, dict_):
toremove = set([ctx for ctx in self.attached_by if ctx.key not in dict_])
toadd = set([Context(key=k, holder=item) for k, v in dict_.items()
for item in itertools.chain(v)])
self.attached_by.update(toadd)
self.attached_by.difference_update(toremove)
class HolderBySetDict(object):
def __init__(self, parent):
self.parent = parent
def __iter__(self):
return iter(self.keys())
def keys(self):
return list(set(ctx.key for ctx in self.parent.attached_by))
def __delitem__(self, key):
toremove = set([ctx for ctx in self.parent.attached_by if ctx.key == key])
self.parent.attached_by.difference_update(toremove)
def __getitem__(self, key):
return HolderBySet(self.parent, key)
def __setitem__(self, key, value):
current = set([ctx for ctx in self.parent.attached_by if ctx.key == key])
toremove = set([ctx for ctx in current if ctx.holder not in value])
toadd = set([Context(key=key,holder=v) for v in value if v not in current])
self.parent.attached_by.update(toadd)
self.parent.attached_by.difference_update(toremove)
# exercises ! for the reader !
#def __contains__(self, key):
#def values(self):
#def items(self):
# ....
class HolderBySet(object):
def __init__(self, parent, key):
self.key = key
self.parent = parent
def __iter__(self):
return iter([ctx.holder for ctx
in self.parent.attached_by if ctx.key == self.key])
def update(self, items):
curr = set([ctx.holder for ctx
in self.parent.attached_by if ctx.key==self.key])
toadd = set(items).difference(curr)
self.parent.attached_by.update(
[Context(key=self.key, holder=item) for item in toadd])
def remove(self, item):
for ctx in self.parent.attached_by:
if ctx.key == self.key and ctx.holder is item:
self.parent.attached_by.remove(ctx)
break
else:
raise ValueError("Value not present")
def add(self, item):
for ctx in self.parent.attached_by:
if ctx.key == self.key and ctx.holder is item:
break
else:
self.parent.attached_by.add(Context(key=self.key, holder=item))
# more exercises ! for the reader !
#def __contains__(self, key):
#def union(self):
#def intersection(self):
#def difference(self):
#def difference_update(self):
# ....
class Context(Base):
__tablename__ = "context"
holder_id = Column(Integer, ForeignKey("a.id"), nullable=False, primary_key=True)
attachment_id = Column(Integer, ForeignKey("a.id"), nullable=False, primary_key=True)
key = Column(String, primary_key=True)
holder = relationship(A,
primaryjoin=lambda: Context.holder_id==A.id)
attachment = relationship(A,
primaryjoin=lambda: Context.attachment_id==A.id,
backref=backref("attached_by", collection_class=set))
a1 = A(1)
a2 = A(2)
a3, a4, a5 = A(3), A(4), A(5)
a1.context["key_1"] = set([a2])
a1.context["key_2"] = set([a3, a4, a5])
assert set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_1"]) == set([a2])
assert set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_2"]) == set([a3, a4, a5])
a10 = A(10)
a1.context["key_1"].add(a10)
print set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_1"])
assert set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_1"]) == set([a2, a10])
a100 = A(100)
a101 = A(101)
a100.context = {
"key_1": set([a101])
}
assert set([ctx.holder for ctx in a100.attached_by]) == set([a101])