how to delete relational item in prisma2 - prisma-graphql

My data model includes the following nodes:
model User {
id Int #id #default(autoincrement())
name String
posts Post[]
}
model Post {
id Int #id #default(autoincrement())
body String
user User #relation(fields: [userId], references: [id])
userId Int
}
I tried to delete one User like this:
async function deleteUser(_, args) {
const { id } = args
return prisma.user.delete({
where: { id: id }
})
}
But it gives an error: ... The change you are trying to make would violate the required relation UserToPost between the User and Post models.
Then how to delete one user? Even I tried to delete the post first then the user but again same error happened.

This has now been released as a preview feature behind a preview feature flag. You can read about it in the release notes for 2.26.0: https://github.com/prisma/prisma/releases/tag/2.26.0
The preview feature can be enabled by setting the preview feature flag referentialActions in the generator block of Prisma Client in your Prisma schema file:
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialActions"]
}

Looks like your table does not support CASCADE deletions and prisma does not automatically add it for you. You will have to manually update the definition of your table either while migration or after the fact.
so basically, alter your table definition.
ALTER TABLE public.Post
DROP CONSTRAINT Post_user_fkey,
ADD CONSTRAINT Post_user_fkey
FOREIGN KEY (user)
REFERENCES public.User(user)
ON DELETE CASCADE
ON UPDATE CASCADE;
Refer to these docs on options of configuring relational queries.

Related

How to Auto Generate of Guid ID in .NET CORE 6?

In my request data, if I have a duplication Guid ID, I want to generate a new Guid ID automatically. How to do it?
public class Roster { public Guid Id {get; set;} }
Here Guid Id is the primary key.
When I made an api post request, what would be the value I give for Guid Id?
If you use SQL and EntityFramework Core you could use this inside your model:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ActivityId { get; set; }
This will tell EF:
this property is the PRIMARY KEY of the table hence the [KEY]
this property should be automatically generated by the database
FYI you need to set a DEFAULT value for you SQL column like so:
(newsequentiaid()) tells SQL that he's in charge of creating a Globally Unique Id everytime you add a record to that table
Don't know if this is the answer you were looking for (nex time provide more info for us) anyway
hope this helps you Cheers!
UPDATE
I do not know if my solution works with MySQL i use it for SQL. Searching a bit online i found no resources to newsequentialid in MySQL database (but i could be wrong, do your own research if you'd like).
Anyway i just don't set it for example:
var activityDB = await context.Activity.FirstOrDefaultAsync(c => c.ActivityId == activity.ActivityId);
if (activityDB == null)
{
activityDB = new Activity();
context.Activity.Add(activityDB);
}
activityDB.Code = activity.Code;
activityDB.Description = activity.Description;
activityDB.Status = activity.Status;
Here's what the code does
check if my id exists if yes i have to edit if is null i don't
create new activity and edit
automatically EF nows what id to handle therefore no need to se it
If there is it means im editing for that id if not will create it automatically

Polymorphism in Prisma Schema - Best practices?

This is more a design question than a coding question. Suppose the following schema:
// schema.prisma
// Solution 1
model Entity {
id Int #id #default(autoincrement())
attrs EntityAttr[]
}
model EntityAttr {
id Int #id #default(autoincrement())
value Json // or String, doesnt matter much here
// the point is I need to attach info on the
// join table of this relation
attr Attr #relation(fields: [attrId], references: [id])
entity Entity #relation(fields: [entityId], references: [id])
entityId Int
attrId Int
##unique([entityId, attrId])
}
model Attr {
id Int #id #default(autoincrement())
entities EntityAttr[]
}
// Solution 2
model Entity {
id Int #id #default(autoincrement())
dateAttrs DateAttr[]
recordAttrs RecordAttr[]
// ... this pattern could continue for more Attr-like models
}
model DateAttr {
id Int #id #default(autoincrement())
name String
entity Entity #relation(fields: [entityId], references: [id])
value DateTime // Stronger typing in generated code
}
model RecordAttr {
// ... define another Entity #relation(...)
name String
value String
// ...
}
// ... and so on
Please note that the schema might not be 100% complete or accurate. It is mainly to get the point across.
Solution 1 has its merits where redundancy and the number of tables in the database is reduced significantly (depending on the number of Attrs). Its downfall comes as confusing queries*, possible case-specific type casting and no code-completion for the value field for each Attr-like model.
* by confusing, I mean that the option for simplified m-n queries in prisma is functionally disabled when using a custom join table (e.g. EntityAttr)
Solution 2 has its merits where the generated code results in more strongly typed code generation for the value field, however it falls in the number of generated tables (I don't actually know if more tables is a good thing or a bad thing, all I think is that if you have similar values, they ought to be in the same table).
What would you do in my shoes?
I was looking pretty long for an appropriate answer and found it here.
I'm not sure if it could be applied to your question, but this is question about prisma and polymorphism, so I think this code snippet might be useful for developers:
model Photo {
id Int #id #default(autoincrement())
likes Like[] #relation("PhotoLike")
}
model Video {
id Int #id #default(autoincrement())
likes Like[] #relation("VideoLike")
}
enum LikableType {
Photo
Video
}
model Like {
id Int #id #default(autoincrement())
Photo Photo? #relation("PhotoLike", fields: [likableId], references: [id], map: "photo_likableId")
Video Video? #relation("VideoLike", fields: [likableId], references: [id], map: "video_likableId")
likableId Int
likableType LikableType
}
Resuling relations in dbdocs:
Sometimes the use case can't be generalized to abstract and have a typing's.
if you control them and has a limited attribute sure you can create each attribute as a separate table each has it is own schema.
Some Times more freedom is needed or the blocks are dynamic.
Use Case: Build A Block Document Editor Like 'notion.so' and you want to let the user create custom blocks or configure them.
you can do it like :
model Document {
id String #id
blocks Block[]
}
model Block {
id String #id
value Json
index Int
customConfig Json?
document Document? #relation(fields: [documentID], references: [id])
documentID String?
blockType BlockType #relation(fields: [blockTypeID], references: [id])
blockTypeID String
}
model BlockType {
id String #id
name String
config Json
blocks Block[]
}
where config and custom config can contains html,custom css classes, link attribute color or anything.
using type script you can create block.types.ts and add different let say templates for the config's .
I hope that I was useful to you, To sum it, it depends on the requirements :>)

GraphQL - Cannot update a table row

Say I have a table Person with attributes id and name. The GraphQL server is all setup by Postgraphile and working as I can query and create new entry. However, I could not update it. Scratching my head over and over again and I am still unable to find out the cause for this.
This is the mutation I tried that failed me every now and then.
mutation($id: Int!, $patch: PersonPatch!) {
updatePersonById(input: { id: $id, patch: $patch }) {
clientMutationId
}
}
The variables
{
id: 1,
patch: {"name": "newname"}
}
I was using Altair GraphQL client to submit the mutation request and the error message returned was "No values were updated in collection 'people' because no values were found."
The person of id = 1 does exist, confirmed by sending a query personById over to get his name. But I just couldn't update his name.
Edit #1
Below is the gql generated by Altair GraphQL Client
updatePersonById(
input: UpdatePersonByIdInput!
): UpdatePersonPayload
input UpdatePersonByIdInput {
clientMutationId: String
patch: PersonPatch!
id: Int!
}
input PersonPatch {
id: Int
name: String
}
Assuming you're using row-level security (RLS) it sounds like the row to be updated does not pass the required security policies for the currently authenticated user.
Here's a small example; you'll want to adjust it to fit your own permissions system
create table person (id serial primary key, name text);
alter table person enable row level security;
grant select, insert(name), update(name), delete on person to graphql;
create policy select_all on person for select using (true);
create policy insert_all on person for insert with check(true);
create policy update_self on person for update using (id = current_person_id());
create policy delete_self on person for delete using (id = current_person_id());
where
create function current_person_id() returns int as $$
select nullif(current_setting('jwt.claims.person_id', true), '')::int;
$$ language sql stable;
If you need more guidance, feel free to drop into the Graphile chat.

Can't Persist Field to Aspnet_Users via NHibernate/ActiveRecord

I'm using ActiveRecord with NHibernate on the backend. I've set up a mapping for Users; I can create/retrieve/register users without any issues.
I now want to add an association called Role to my users (many users per role). I've created the appropriate Role class, tables, data, etc. and everything seems to be working on that end as well.
The problem is that when I save a user and associate a Role, that association does not persist to the database.
I've added a RoleId (int16) column to the aspnet_Users table to match the Role table's Id (int16) column. I've tried using Save and SaveAndFlush without success.
Here's some code:
Role superUser = Role.First(r => r.name == "Super User");
User me = User.First(r => r.UserName == myUserName);
me.Role = superUser;
me.Save(); // Or: SaveAndFlush
When debugging, I can see the association on the objects when they're saved (i.e. me.Role is not null and has the right attributes/properties/etc.) However, when I look at the database, the RoleId value for that user is still NULL. (SaveAndFlush doesn't make a difference.)
What am I missing?
I've read somewhere on SO that extending the users table is usually done by adding another table and linking the two by a foreign key; I assume the classes would then use inheritance by composition for the new ExtendedUser class. Assuming I don't want to go that route, why isn't this working? Is it because of the specific ASP.NET MVC stored procedures et. all?
Some relevant mapping:
[ActiveRecord("aspnet_Users", Mutable = false)]
public class User : ActiveRecordLinqBase<User>
{
[PrimaryKey(PrimaryKeyType.Assigned)]
public Guid UserId { get; set; }
// ...
[BelongsTo("RoleId", Cascade = CascadeEnum.SaveUpdate)]
public Role Role { get; set; }
}
[ActiveRecord]
public class Role : ActiveRecordLinqBase<Role>
{
[PrimaryKey]
public int Id { get; set; }
// ...
[HasMany(Inverse = true)]
public IList<User> Users { get; set; }
[Property]
public string Name { get; set; }
}
Edit: mutable="false" - this clearly stands that entity is read only, which is the source of your problem.
Immutable classes, mutable="false", may not be updated or deleted by the application. This allows NHibernate to make some minor
performance optimizations.
Also:
I believe that you need to have cascading defined. You are not saving just the entity itself but also reference to other entity. Use attributes, fluent config or hbml to define this the way you need. Here are the cascading options:
Here is what each cascade option means:
none - do not do any cascades, let the users handles them by
themselves.
save-update - when the object is saved/updated, check the assoications and save/update any object that require it (including
save/update the assoications in many-to-many scenario).
delete - when the object is deleted, delete all the objects in the assoication.
delete-orphan - when the object is deleted, delete all the objects in the assoication. In addition to that, when an object is
removed from the assoication and not assoicated with another object
(orphaned), also delete it.
all - when an object is save/update/delete, check the assoications and save/update/delete all the objects found.
all-delete-orphan - when an object is save/update/delete, check the assoications and save/update/delete all the objects found. In additional to that, when an object is removed from the assoication and not assoicated with another object (orphaned), also delete it.
You may want to read this article.

Custom columns in list view in Symfony admin generator

tl;dr: I need to show data from two different tables in the list view of the Symfony admin generator (preferably via a JOIN statement)
I'm using the sfDoctrineGuardPlugin to manage the users of my app. Each user also has their own profile that they can fill out. The schema (schema.yml) for the profile table looks like this:
sfGuardUserProfile:
connection: doctrine
tableName: sf_guard_user_profile
columns:
sf_guard_user_id: integer(8)
client_id: string(128)
relations:
sfGuardUser:
local: sf_guard_user_id
foreign: id
foreignType: one
onDelete: CASCADE
Right now table is a client_id and a foreign key to the id column on the sfGuardUser table.
(Yes, I know I need an id column in there just for DB accessibility, and more is coming later)
I've created an admin for my profile table using the Symfony admin generator. I would like my list view on the admin to look like this (generator.yml):
config:
...
list:
title: Clients
display: [client_id, sf_guard_user_id, username]
table_method: retrieveProfileList
However, my username column does not work, and I receive a Doctrine_Record_UnkownPropertyException of:
Unknown record property / related component "username" on "sfGuardUserProfile"
I was hoping a custom table_method to populate the data for the list view would help me, but my table method (in lib/model/sfGuardUserProfileTable.class.php) does not seem to help. I followed an example in the Symfony Book, and this is the method:
public static function retrieveProfileList(Doctrine_Query $q)
{
$rootAlias = $q->getRootAlias();
$q->leftJoin($rootAlias . '.sfGuardUser id');
return $q;
}
Any ideas on how to have the Symfony admin generator display a list view based on a join? Or, alternatively, how to pull from a separate model?
I have answered my own question!
I changed the join method on my sfGuardUserProfileTable class to something I could better understand (thanks to this helpful link):
public static function retrieveProfileList(Doctrine_Query $q)
{
return $q->select('r.*, u.username')
->leftJoin('r.sfGuardUser u');
}
And threw in a getUsername method in my sfGuardUserProfile model class
public function getUsername()
{
return $this->sfGuardUser->username;
}
Thanks for being my rubber duck, SO!

Resources