Giter Club home page Giter Club logo

Comments (6)

sgrif avatar sgrif commented on May 18, 2024

In addition to just flat out fixing this problem, I'd also like to decouple the table name from the struct name, and allow a #[table_name="foo"] annotation.

from diesel.

mcasper avatar mcasper commented on May 18, 2024

The decoupling would probably help a lot, as it looks like it has trouble with any edge case pluralization as well

from diesel.

sgrif avatar sgrif commented on May 18, 2024

Yeah, it literally is .to_lowercase + s right now. We can make it
slightly better, but I don't want to try and handle actual pluralization
for every word

On Mon, Jan 11, 2016, 5:46 PM Matt Casper [email protected] wrote:

The decoupling would probably help a lot, as it looks like it has trouble
with any edge case pluralization as well


Reply to this email directly or view it on GitHub
#86 (comment).

from diesel.

mfpiccolo avatar mfpiccolo commented on May 18, 2024

👍 for #[table_name="foo"]

from diesel.

sgrif avatar sgrif commented on May 18, 2024

Also I didn't mention it earlier, but also keep in mind that I've left associations out of the public API of the released crate, as I'm not happy with the current design and it may change further (it also likely requires the lattice rule coming w/ specialization to implement properly)

from diesel.

sgrif avatar sgrif commented on May 18, 2024

So I've been starting to think more heavily about this, and specifically how to avoid duplicating a lot of information when dealing with non-standard tables and primary keys. An interesting thing about the #[table_name] annotation is that I can't actually access it from annotations on other items (which makes sense, especially when considering the associated struct might be coming from another crate).

Just spitballing some thoughts here. Scoping to belongs_to only to simplify. We might be able to work this into assuming the other side implements a trait, rather than how we operate today. If we do #[belongs_to(bar)] Struct Foo;, then the code that'll get generated is:

impl ::diesel::BelongingToDsl<Bar> for Foo {
    type Output = ::diesel::helper_types::FindBy<
        foos::table,
        foos::bar_id,
        i32,
    >;

    fn belonging_to(model: &Bar) -> Self::Output {
        foos::table.filter(foos::bar_id.eq(model.id.clone()))
    }
}

impl ::diesel::JoinTo<bars::table> for foos::table {
    fn join_sql(&self, out: &mut ::diesel::query_builder::QueryBuilder)
        -> ::diesel::query_builder::BuildQueryResult
    {
        try!(bars::table.from_clause(out));
        out.push_sql(" ON ");
        foos::bar_id.eq(bars::table.primary_key()).to_sql(out)
    }
}

So I guess really all we need is something like

trait Identifiable {
    type Table: Table;
    type Pk: AsExpression<Self::Table::PrimaryKey::SqlType>;

    fn table() -> Self::Table;
    fn primary_key(&self) -> Self::Pk;
}

Now the question is where do we actually generate this. Are we fine with just requiring a #derive(Identifiable) on the other side? I could just tack it onto something common like Queriable, but that would imply that you don't have models which aren't associated with a single row of a single table.

It's worth noting that all of this is focusing around the idea that these structs will represent a single row on a single table. While that's counter to the design of the framework overall, I think that more or less has to be a given when we start talking about associations (unless we want to explore the idea that you'd have a single struct to represent both sides, but I don't think that'll work well with one-to-many, and would inhibit code re-use).

It's also worth noting that we never actually use the argument to belongs_to, other than to generate the foreign key and infer the struct name.

With all of this in mind, I'm thinking that we should change the API to #[belongs_to(User)], with the ability to specify the foreign key: #[belongs_to(User, foreign_key="author_id")]. With this change, the generated code would become:

impl ::diesel::BelongingToDsl<Bar> for Foo {
    type Output = ::diesel::helper_types::FindBy<
        foos::table,
        foos::bar_id,
        i32,
    >;

    fn belonging_to(model: &Bar) -> Self::Output {
        foos::table.filter(foos::bar_id.eq(<Bar as Identifiable>::primary_key(model)))
    }
}

impl ::diesel::JoinTo<<Bar as Identifiable>::Table> for foos::table {
    fn join_sql(&self, out: &mut ::diesel::query_builder::QueryBuilder)
        -> ::diesel::query_builder::BuildQueryResult
    {
        try!(<Bar as Identifiable>::table().from_clause(out));
        out.push_sql(" ON ");
        foos::bar_id.eq(<Bar as Identifiable>::table().primary_key()).to_sql(out)
    }
}

This does mean that you can't have an association using a primary key other than that of the table. I think that's fine, as the point is that we're equating with a single row, and I've never seen a good use case for changing the primary key of an association in Rails.

This will be a little bit more murky when it comes to has_many, as that means the API becomes the slightly awkward #[has_many(Post)] or #[has_many(Post, foreign_key="post_id")]. It's worth noting that has_many as it is today is effectively worthless (other than enabling joining).

At some point it will also be the thing that makes you able to get Vec<(User, Vec<Post>)> out of a query, but I'm not sure that needs to care about the struct as much as it is a property of how you're performing the join. I was writing some examples of what I mean but as I wrote them I realized there's some flaws involving how it gets mixed with select. I'd have to implement to figure that out. But what I'm getting at is that unlike belongs_to, which does deal with a specific struct, has_many doesn't necessarily need to know about it, and in fact it is more about the table, implying that #[has_many(posts)] is in fact the right thing to do.

Perhaps at the heart of this is figuring out exactly what associations are meant to do. I do like Post::belonging_to(user) as a helpful piece of sugar over the common posts.filter(user_id.eq(user.id)) (I also just realized that could totally be posts::belonging_to(user) since the Post struct has nothing to do with belonging_to, but I'm struggling to come up with any cases where we need to involve structs.

If that's the case, maybe we need to separate out the relationships between tables from the relationships between structs. The biggest problem there is that we no longer have a place for you to actually annotate a table. I need to spike some more on what I want to be able to do with has_many to figure out the right place to solve this...

from diesel.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.