Browse Source

First commit: https://entgo.io/docs/getting-started/

Frederic G. MARAND 4 years ago
parent
commit
744aea335e

+ 3 - 0
.gitignore

@@ -3,6 +3,7 @@
 *.o
 *.a
 *.so
+ent
 
 # Folders
 _obj
@@ -24,3 +25,5 @@ _testmain.go
 *.test
 *.prof
 
+# Data
+*.db

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/

+ 12 - 0
.idea/dataSources.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
+    <data-source source="LOCAL" name="Entdemo" read-only="true" single-connection="true" uuid="6ea0b074-644c-4132-85ff-9b199057d601">
+      <driver-ref>sqlite.xerial</driver-ref>
+      <synchronize>true</synchronize>
+      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
+      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/ent.db</jdbc-url>
+      <auto-close enable="true" />
+    </data-source>
+  </component>
+</project>

+ 9 - 0
.idea/entdemo.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/entdemo.iml" filepath="$PROJECT_DIR$/.idea/entdemo.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 202 - 0
LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 146 - 0
ent/car.go

@@ -0,0 +1,146 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+)
+
+// Car is the model entity for the Car schema.
+type Car struct {
+	config `json:"-"`
+	// ID of the ent.
+	ID int `json:"id,omitempty"`
+	// Model holds the value of the "model" field.
+	Model string `json:"model,omitempty"`
+	// RegisteredAt holds the value of the "registered_at" field.
+	RegisteredAt time.Time `json:"registered_at,omitempty"`
+	// Edges holds the relations/edges for other nodes in the graph.
+	// The values are being populated by the CarQuery when eager-loading is set.
+	Edges     CarEdges `json:"edges"`
+	user_cars *int
+}
+
+// CarEdges holds the relations/edges for other nodes in the graph.
+type CarEdges struct {
+	// Owner holds the value of the owner edge.
+	Owner *User
+	// loadedTypes holds the information for reporting if a
+	// type was loaded (or requested) in eager-loading or not.
+	loadedTypes [1]bool
+}
+
+// OwnerOrErr returns the Owner value or an error if the edge
+// was not loaded in eager-loading, or loaded but was not found.
+func (e CarEdges) OwnerOrErr() (*User, error) {
+	if e.loadedTypes[0] {
+		if e.Owner == nil {
+			// The edge owner was loaded in eager-loading,
+			// but was not found.
+			return nil, &NotFoundError{label: user.Label}
+		}
+		return e.Owner, nil
+	}
+	return nil, &NotLoadedError{edge: "owner"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*Car) scanValues() []interface{} {
+	return []interface{}{
+		&sql.NullInt64{},  // id
+		&sql.NullString{}, // model
+		&sql.NullTime{},   // registered_at
+	}
+}
+
+// fkValues returns the types for scanning foreign-keys values from sql.Rows.
+func (*Car) fkValues() []interface{} {
+	return []interface{}{
+		&sql.NullInt64{}, // user_cars
+	}
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the Car fields.
+func (c *Car) assignValues(values ...interface{}) error {
+	if m, n := len(values), len(car.Columns); m < n {
+		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+	}
+	value, ok := values[0].(*sql.NullInt64)
+	if !ok {
+		return fmt.Errorf("unexpected type %T for field id", value)
+	}
+	c.ID = int(value.Int64)
+	values = values[1:]
+	if value, ok := values[0].(*sql.NullString); !ok {
+		return fmt.Errorf("unexpected type %T for field model", values[0])
+	} else if value.Valid {
+		c.Model = value.String
+	}
+	if value, ok := values[1].(*sql.NullTime); !ok {
+		return fmt.Errorf("unexpected type %T for field registered_at", values[1])
+	} else if value.Valid {
+		c.RegisteredAt = value.Time
+	}
+	values = values[2:]
+	if len(values) == len(car.ForeignKeys) {
+		if value, ok := values[0].(*sql.NullInt64); !ok {
+			return fmt.Errorf("unexpected type %T for edge-field user_cars", value)
+		} else if value.Valid {
+			c.user_cars = new(int)
+			*c.user_cars = int(value.Int64)
+		}
+	}
+	return nil
+}
+
+// QueryOwner queries the owner edge of the Car.
+func (c *Car) QueryOwner() *UserQuery {
+	return (&CarClient{config: c.config}).QueryOwner(c)
+}
+
+// Update returns a builder for updating this Car.
+// Note that, you need to call Car.Unwrap() before calling this method, if this Car
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (c *Car) Update() *CarUpdateOne {
+	return (&CarClient{config: c.config}).UpdateOne(c)
+}
+
+// Unwrap unwraps the entity that was returned from a transaction after it was closed,
+// so that all next queries will be executed through the driver which created the transaction.
+func (c *Car) Unwrap() *Car {
+	tx, ok := c.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: Car is not a transactional entity")
+	}
+	c.config.driver = tx.drv
+	return c
+}
+
+// String implements the fmt.Stringer.
+func (c *Car) String() string {
+	var builder strings.Builder
+	builder.WriteString("Car(")
+	builder.WriteString(fmt.Sprintf("id=%v", c.ID))
+	builder.WriteString(", model=")
+	builder.WriteString(c.Model)
+	builder.WriteString(", registered_at=")
+	builder.WriteString(c.RegisteredAt.Format(time.ANSIC))
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// Cars is a parsable slice of Car.
+type Cars []*Car
+
+func (c Cars) config(cfg config) {
+	for _i := range c {
+		c[_i].config = cfg
+	}
+}

+ 37 - 0
ent/car/car.go

@@ -0,0 +1,37 @@
+// Code generated by entc, DO NOT EDIT.
+
+package car
+
+const (
+	// Label holds the string label denoting the car type in the database.
+	Label = "car"
+	// FieldID holds the string denoting the id field in the database.
+	FieldID           = "id"    // FieldModel holds the string denoting the model vertex property in the database.
+	FieldModel        = "model" // FieldRegisteredAt holds the string denoting the registered_at vertex property in the database.
+	FieldRegisteredAt = "registered_at"
+
+	// EdgeOwner holds the string denoting the owner edge name in mutations.
+	EdgeOwner = "owner"
+
+	// Table holds the table name of the car in the database.
+	Table = "cars"
+	// OwnerTable is the table the holds the owner relation/edge.
+	OwnerTable = "cars"
+	// OwnerInverseTable is the table name for the User entity.
+	// It exists in this package in order to avoid circular dependency with the "user" package.
+	OwnerInverseTable = "users"
+	// OwnerColumn is the table column denoting the owner relation/edge.
+	OwnerColumn = "user_cars"
+)
+
+// Columns holds all SQL columns for car fields.
+var Columns = []string{
+	FieldID,
+	FieldModel,
+	FieldRegisteredAt,
+}
+
+// ForeignKeys holds the SQL foreign-keys that are owned by the Car type.
+var ForeignKeys = []string{
+	"user_cars",
+}

+ 355 - 0
ent/car/where.go

@@ -0,0 +1,355 @@
+// Code generated by entc, DO NOT EDIT.
+
+package car
+
+import (
+	"time"
+
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+)
+
+// ID filters vertices based on their identifier.
+func ID(id int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldID), id))
+	})
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldID), id))
+	})
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldID), id))
+	})
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(ids) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		v := make([]interface{}, len(ids))
+		for i := range v {
+			v[i] = ids[i]
+		}
+		s.Where(sql.In(s.C(FieldID), v...))
+	})
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(ids) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		v := make([]interface{}, len(ids))
+		for i := range v {
+			v[i] = ids[i]
+		}
+		s.Where(sql.NotIn(s.C(FieldID), v...))
+	})
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldID), id))
+	})
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldID), id))
+	})
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldID), id))
+	})
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id int) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldID), id))
+	})
+}
+
+// Model applies equality check predicate on the "model" field. It's identical to ModelEQ.
+func Model(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldModel), v))
+	})
+}
+
+// RegisteredAt applies equality check predicate on the "registered_at" field. It's identical to RegisteredAtEQ.
+func RegisteredAt(v time.Time) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldRegisteredAt), v))
+	})
+}
+
+// ModelEQ applies the EQ predicate on the "model" field.
+func ModelEQ(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldModel), v))
+	})
+}
+
+// ModelNEQ applies the NEQ predicate on the "model" field.
+func ModelNEQ(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldModel), v))
+	})
+}
+
+// ModelIn applies the In predicate on the "model" field.
+func ModelIn(vs ...string) predicate.Car {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Car(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldModel), v...))
+	})
+}
+
+// ModelNotIn applies the NotIn predicate on the "model" field.
+func ModelNotIn(vs ...string) predicate.Car {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Car(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldModel), v...))
+	})
+}
+
+// ModelGT applies the GT predicate on the "model" field.
+func ModelGT(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldModel), v))
+	})
+}
+
+// ModelGTE applies the GTE predicate on the "model" field.
+func ModelGTE(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldModel), v))
+	})
+}
+
+// ModelLT applies the LT predicate on the "model" field.
+func ModelLT(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldModel), v))
+	})
+}
+
+// ModelLTE applies the LTE predicate on the "model" field.
+func ModelLTE(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldModel), v))
+	})
+}
+
+// ModelContains applies the Contains predicate on the "model" field.
+func ModelContains(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.Contains(s.C(FieldModel), v))
+	})
+}
+
+// ModelHasPrefix applies the HasPrefix predicate on the "model" field.
+func ModelHasPrefix(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.HasPrefix(s.C(FieldModel), v))
+	})
+}
+
+// ModelHasSuffix applies the HasSuffix predicate on the "model" field.
+func ModelHasSuffix(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.HasSuffix(s.C(FieldModel), v))
+	})
+}
+
+// ModelEqualFold applies the EqualFold predicate on the "model" field.
+func ModelEqualFold(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.EqualFold(s.C(FieldModel), v))
+	})
+}
+
+// ModelContainsFold applies the ContainsFold predicate on the "model" field.
+func ModelContainsFold(v string) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.ContainsFold(s.C(FieldModel), v))
+	})
+}
+
+// RegisteredAtEQ applies the EQ predicate on the "registered_at" field.
+func RegisteredAtEQ(v time.Time) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldRegisteredAt), v))
+	})
+}
+
+// RegisteredAtNEQ applies the NEQ predicate on the "registered_at" field.
+func RegisteredAtNEQ(v time.Time) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldRegisteredAt), v))
+	})
+}
+
+// RegisteredAtIn applies the In predicate on the "registered_at" field.
+func RegisteredAtIn(vs ...time.Time) predicate.Car {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Car(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldRegisteredAt), v...))
+	})
+}
+
+// RegisteredAtNotIn applies the NotIn predicate on the "registered_at" field.
+func RegisteredAtNotIn(vs ...time.Time) predicate.Car {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Car(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldRegisteredAt), v...))
+	})
+}
+
+// RegisteredAtGT applies the GT predicate on the "registered_at" field.
+func RegisteredAtGT(v time.Time) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldRegisteredAt), v))
+	})
+}
+
+// RegisteredAtGTE applies the GTE predicate on the "registered_at" field.
+func RegisteredAtGTE(v time.Time) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldRegisteredAt), v))
+	})
+}
+
+// RegisteredAtLT applies the LT predicate on the "registered_at" field.
+func RegisteredAtLT(v time.Time) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldRegisteredAt), v))
+	})
+}
+
+// RegisteredAtLTE applies the LTE predicate on the "registered_at" field.
+func RegisteredAtLTE(v time.Time) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldRegisteredAt), v))
+	})
+}
+
+// HasOwner applies the HasEdge predicate on the "owner" edge.
+func HasOwner() predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(OwnerTable, FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, OwnerTable, OwnerColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasOwnerWith applies the HasEdge predicate on the "owner" edge with a given conditions (other predicates).
+func HasOwnerWith(preds ...predicate.User) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(OwnerInverseTable, FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, OwnerTable, OwnerColumn),
+		)
+		sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+			for _, p := range preds {
+				p(s)
+			}
+		})
+	})
+}
+
+// And groups list of predicates with the AND operator between them.
+func And(predicates ...predicate.Car) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s1 := s.Clone().SetP(nil)
+		for _, p := range predicates {
+			p(s1)
+		}
+		s.Where(s1.P())
+	})
+}
+
+// Or groups list of predicates with the OR operator between them.
+func Or(predicates ...predicate.Car) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		s1 := s.Clone().SetP(nil)
+		for i, p := range predicates {
+			if i > 0 {
+				s1.Or()
+			}
+			p(s1)
+		}
+		s.Where(s1.P())
+	})
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.Car) predicate.Car {
+	return predicate.Car(func(s *sql.Selector) {
+		p(s.Not())
+	})
+}

+ 153 - 0
ent/car_create.go

@@ -0,0 +1,153 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// CarCreate is the builder for creating a Car entity.
+type CarCreate struct {
+	config
+	mutation *CarMutation
+	hooks    []Hook
+}
+
+// SetModel sets the model field.
+func (cc *CarCreate) SetModel(s string) *CarCreate {
+	cc.mutation.SetModel(s)
+	return cc
+}
+
+// SetRegisteredAt sets the registered_at field.
+func (cc *CarCreate) SetRegisteredAt(t time.Time) *CarCreate {
+	cc.mutation.SetRegisteredAt(t)
+	return cc
+}
+
+// SetOwnerID sets the owner edge to User by id.
+func (cc *CarCreate) SetOwnerID(id int) *CarCreate {
+	cc.mutation.SetOwnerID(id)
+	return cc
+}
+
+// SetNillableOwnerID sets the owner edge to User by id if the given value is not nil.
+func (cc *CarCreate) SetNillableOwnerID(id *int) *CarCreate {
+	if id != nil {
+		cc = cc.SetOwnerID(*id)
+	}
+	return cc
+}
+
+// SetOwner sets the owner edge to User.
+func (cc *CarCreate) SetOwner(u *User) *CarCreate {
+	return cc.SetOwnerID(u.ID)
+}
+
+// Save creates the Car in the database.
+func (cc *CarCreate) Save(ctx context.Context) (*Car, error) {
+	if _, ok := cc.mutation.Model(); !ok {
+		return nil, errors.New("ent: missing required field \"model\"")
+	}
+	if _, ok := cc.mutation.RegisteredAt(); !ok {
+		return nil, errors.New("ent: missing required field \"registered_at\"")
+	}
+	var (
+		err  error
+		node *Car
+	)
+	if len(cc.hooks) == 0 {
+		node, err = cc.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*CarMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			cc.mutation = mutation
+			node, err = cc.sqlSave(ctx)
+			return node, err
+		})
+		for i := len(cc.hooks) - 1; i >= 0; i-- {
+			mut = cc.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, cc.mutation); err != nil {
+			return nil, err
+		}
+	}
+	return node, err
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (cc *CarCreate) SaveX(ctx context.Context) *Car {
+	v, err := cc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (cc *CarCreate) sqlSave(ctx context.Context) (*Car, error) {
+	var (
+		c     = &Car{config: cc.config}
+		_spec = &sqlgraph.CreateSpec{
+			Table: car.Table,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: car.FieldID,
+			},
+		}
+	)
+	if value, ok := cc.mutation.Model(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: car.FieldModel,
+		})
+		c.Model = value
+	}
+	if value, ok := cc.mutation.RegisteredAt(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeTime,
+			Value:  value,
+			Column: car.FieldRegisteredAt,
+		})
+		c.RegisteredAt = value
+	}
+	if nodes := cc.mutation.OwnerIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   car.OwnerTable,
+			Columns: []string{car.OwnerColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges = append(_spec.Edges, edge)
+	}
+	if err := sqlgraph.CreateNode(ctx, cc.driver, _spec); err != nil {
+		if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return nil, err
+	}
+	id := _spec.ID.Value.(int64)
+	c.ID = int(id)
+	return c, nil
+}

+ 108 - 0
ent/car_delete.go

@@ -0,0 +1,108 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// CarDelete is the builder for deleting a Car entity.
+type CarDelete struct {
+	config
+	hooks      []Hook
+	mutation   *CarMutation
+	predicates []predicate.Car
+}
+
+// Where adds a new predicate to the delete builder.
+func (cd *CarDelete) Where(ps ...predicate.Car) *CarDelete {
+	cd.predicates = append(cd.predicates, ps...)
+	return cd
+}
+
+// Exec executes the deletion query and returns how many vertices were deleted.
+func (cd *CarDelete) Exec(ctx context.Context) (int, error) {
+	var (
+		err      error
+		affected int
+	)
+	if len(cd.hooks) == 0 {
+		affected, err = cd.sqlExec(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*CarMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			cd.mutation = mutation
+			affected, err = cd.sqlExec(ctx)
+			return affected, err
+		})
+		for i := len(cd.hooks) - 1; i >= 0; i-- {
+			mut = cd.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, cd.mutation); err != nil {
+			return 0, err
+		}
+	}
+	return affected, err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (cd *CarDelete) ExecX(ctx context.Context) int {
+	n, err := cd.Exec(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return n
+}
+
+func (cd *CarDelete) sqlExec(ctx context.Context) (int, error) {
+	_spec := &sqlgraph.DeleteSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table: car.Table,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: car.FieldID,
+			},
+		},
+	}
+	if ps := cd.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return sqlgraph.DeleteNodes(ctx, cd.driver, _spec)
+}
+
+// CarDeleteOne is the builder for deleting a single Car entity.
+type CarDeleteOne struct {
+	cd *CarDelete
+}
+
+// Exec executes the deletion query.
+func (cdo *CarDeleteOne) Exec(ctx context.Context) error {
+	n, err := cdo.cd.Exec(ctx)
+	switch {
+	case err != nil:
+		return err
+	case n == 0:
+		return &NotFoundError{car.Label}
+	default:
+		return nil
+	}
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (cdo *CarDeleteOne) ExecX(ctx context.Context) {
+	cdo.cd.ExecX(ctx)
+}

+ 681 - 0
ent/car_query.go

@@ -0,0 +1,681 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"math"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// CarQuery is the builder for querying Car entities.
+type CarQuery struct {
+	config
+	limit      *int
+	offset     *int
+	order      []Order
+	unique     []string
+	predicates []predicate.Car
+	// eager-loading edges.
+	withOwner *UserQuery
+	withFKs   bool
+	// intermediate query.
+	sql *sql.Selector
+}
+
+// Where adds a new predicate for the builder.
+func (cq *CarQuery) Where(ps ...predicate.Car) *CarQuery {
+	cq.predicates = append(cq.predicates, ps...)
+	return cq
+}
+
+// Limit adds a limit step to the query.
+func (cq *CarQuery) Limit(limit int) *CarQuery {
+	cq.limit = &limit
+	return cq
+}
+
+// Offset adds an offset step to the query.
+func (cq *CarQuery) Offset(offset int) *CarQuery {
+	cq.offset = &offset
+	return cq
+}
+
+// Order adds an order step to the query.
+func (cq *CarQuery) Order(o ...Order) *CarQuery {
+	cq.order = append(cq.order, o...)
+	return cq
+}
+
+// QueryOwner chains the current query on the owner edge.
+func (cq *CarQuery) QueryOwner() *UserQuery {
+	query := &UserQuery{config: cq.config}
+	step := sqlgraph.NewStep(
+		sqlgraph.From(car.Table, car.FieldID, cq.sqlQuery()),
+		sqlgraph.To(user.Table, user.FieldID),
+		sqlgraph.Edge(sqlgraph.M2O, true, car.OwnerTable, car.OwnerColumn),
+	)
+	query.sql = sqlgraph.SetNeighbors(cq.driver.Dialect(), step)
+	return query
+}
+
+// First returns the first Car entity in the query. Returns *NotFoundError when no car was found.
+func (cq *CarQuery) First(ctx context.Context) (*Car, error) {
+	cs, err := cq.Limit(1).All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	if len(cs) == 0 {
+		return nil, &NotFoundError{car.Label}
+	}
+	return cs[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (cq *CarQuery) FirstX(ctx context.Context) *Car {
+	c, err := cq.First(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return c
+}
+
+// FirstID returns the first Car id in the query. Returns *NotFoundError when no id was found.
+func (cq *CarQuery) FirstID(ctx context.Context) (id int, err error) {
+	var ids []int
+	if ids, err = cq.Limit(1).IDs(ctx); err != nil {
+		return
+	}
+	if len(ids) == 0 {
+		err = &NotFoundError{car.Label}
+		return
+	}
+	return ids[0], nil
+}
+
+// FirstXID is like FirstID, but panics if an error occurs.
+func (cq *CarQuery) FirstXID(ctx context.Context) int {
+	id, err := cq.FirstID(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return id
+}
+
+// Only returns the only Car entity in the query, returns an error if not exactly one entity was returned.
+func (cq *CarQuery) Only(ctx context.Context) (*Car, error) {
+	cs, err := cq.Limit(2).All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	switch len(cs) {
+	case 1:
+		return cs[0], nil
+	case 0:
+		return nil, &NotFoundError{car.Label}
+	default:
+		return nil, &NotSingularError{car.Label}
+	}
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (cq *CarQuery) OnlyX(ctx context.Context) *Car {
+	c, err := cq.Only(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
+
+// OnlyID returns the only Car id in the query, returns an error if not exactly one id was returned.
+func (cq *CarQuery) OnlyID(ctx context.Context) (id int, err error) {
+	var ids []int
+	if ids, err = cq.Limit(2).IDs(ctx); err != nil {
+		return
+	}
+	switch len(ids) {
+	case 1:
+		id = ids[0]
+	case 0:
+		err = &NotFoundError{car.Label}
+	default:
+		err = &NotSingularError{car.Label}
+	}
+	return
+}
+
+// OnlyXID is like OnlyID, but panics if an error occurs.
+func (cq *CarQuery) OnlyXID(ctx context.Context) int {
+	id, err := cq.OnlyID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// All executes the query and returns a list of Cars.
+func (cq *CarQuery) All(ctx context.Context) ([]*Car, error) {
+	return cq.sqlAll(ctx)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (cq *CarQuery) AllX(ctx context.Context) []*Car {
+	cs, err := cq.All(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return cs
+}
+
+// IDs executes the query and returns a list of Car ids.
+func (cq *CarQuery) IDs(ctx context.Context) ([]int, error) {
+	var ids []int
+	if err := cq.Select(car.FieldID).Scan(ctx, &ids); err != nil {
+		return nil, err
+	}
+	return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (cq *CarQuery) IDsX(ctx context.Context) []int {
+	ids, err := cq.IDs(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return ids
+}
+
+// Count returns the count of the given query.
+func (cq *CarQuery) Count(ctx context.Context) (int, error) {
+	return cq.sqlCount(ctx)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (cq *CarQuery) CountX(ctx context.Context) int {
+	count, err := cq.Count(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (cq *CarQuery) Exist(ctx context.Context) (bool, error) {
+	return cq.sqlExist(ctx)
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (cq *CarQuery) ExistX(ctx context.Context) bool {
+	exist, err := cq.Exist(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return exist
+}
+
+// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (cq *CarQuery) Clone() *CarQuery {
+	return &CarQuery{
+		config:     cq.config,
+		limit:      cq.limit,
+		offset:     cq.offset,
+		order:      append([]Order{}, cq.order...),
+		unique:     append([]string{}, cq.unique...),
+		predicates: append([]predicate.Car{}, cq.predicates...),
+		// clone intermediate query.
+		sql: cq.sql.Clone(),
+	}
+}
+
+//  WithOwner tells the query-builder to eager-loads the nodes that are connected to
+// the "owner" edge. The optional arguments used to configure the query builder of the edge.
+func (cq *CarQuery) WithOwner(opts ...func(*UserQuery)) *CarQuery {
+	query := &UserQuery{config: cq.config}
+	for _, opt := range opts {
+		opt(query)
+	}
+	cq.withOwner = query
+	return cq
+}
+
+// GroupBy used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+//	var v []struct {
+//		Model string `json:"model,omitempty"`
+//		Count int `json:"count,omitempty"`
+//	}
+//
+//	client.Car.Query().
+//		GroupBy(car.FieldModel).
+//		Aggregate(ent.Count()).
+//		Scan(ctx, &v)
+//
+func (cq *CarQuery) GroupBy(field string, fields ...string) *CarGroupBy {
+	group := &CarGroupBy{config: cq.config}
+	group.fields = append([]string{field}, fields...)
+	group.sql = cq.sqlQuery()
+	return group
+}
+
+// Select one or more fields from the given query.
+//
+// Example:
+//
+//	var v []struct {
+//		Model string `json:"model,omitempty"`
+//	}
+//
+//	client.Car.Query().
+//		Select(car.FieldModel).
+//		Scan(ctx, &v)
+//
+func (cq *CarQuery) Select(field string, fields ...string) *CarSelect {
+	selector := &CarSelect{config: cq.config}
+	selector.fields = append([]string{field}, fields...)
+	selector.sql = cq.sqlQuery()
+	return selector
+}
+
+func (cq *CarQuery) sqlAll(ctx context.Context) ([]*Car, error) {
+	var (
+		nodes       = []*Car{}
+		withFKs     = cq.withFKs
+		_spec       = cq.querySpec()
+		loadedTypes = [1]bool{
+			cq.withOwner != nil,
+		}
+	)
+	if cq.withOwner != nil {
+		withFKs = true
+	}
+	if withFKs {
+		_spec.Node.Columns = append(_spec.Node.Columns, car.ForeignKeys...)
+	}
+	_spec.ScanValues = func() []interface{} {
+		node := &Car{config: cq.config}
+		nodes = append(nodes, node)
+		values := node.scanValues()
+		if withFKs {
+			values = append(values, node.fkValues()...)
+		}
+		return values
+	}
+	_spec.Assign = func(values ...interface{}) error {
+		if len(nodes) == 0 {
+			return fmt.Errorf("ent: Assign called without calling ScanValues")
+		}
+		node := nodes[len(nodes)-1]
+		node.Edges.loadedTypes = loadedTypes
+		return node.assignValues(values...)
+	}
+	if err := sqlgraph.QueryNodes(ctx, cq.driver, _spec); err != nil {
+		return nil, err
+	}
+	if len(nodes) == 0 {
+		return nodes, nil
+	}
+
+	if query := cq.withOwner; query != nil {
+		ids := make([]int, 0, len(nodes))
+		nodeids := make(map[int][]*Car)
+		for i := range nodes {
+			if fk := nodes[i].user_cars; fk != nil {
+				ids = append(ids, *fk)
+				nodeids[*fk] = append(nodeids[*fk], nodes[i])
+			}
+		}
+		query.Where(user.IDIn(ids...))
+		neighbors, err := query.All(ctx)
+		if err != nil {
+			return nil, err
+		}
+		for _, n := range neighbors {
+			nodes, ok := nodeids[n.ID]
+			if !ok {
+				return nil, fmt.Errorf(`unexpected foreign-key "user_cars" returned %v`, n.ID)
+			}
+			for i := range nodes {
+				nodes[i].Edges.Owner = n
+			}
+		}
+	}
+
+	return nodes, nil
+}
+
+func (cq *CarQuery) sqlCount(ctx context.Context) (int, error) {
+	_spec := cq.querySpec()
+	return sqlgraph.CountNodes(ctx, cq.driver, _spec)
+}
+
+func (cq *CarQuery) sqlExist(ctx context.Context) (bool, error) {
+	n, err := cq.sqlCount(ctx)
+	if err != nil {
+		return false, fmt.Errorf("ent: check existence: %v", err)
+	}
+	return n > 0, nil
+}
+
+func (cq *CarQuery) querySpec() *sqlgraph.QuerySpec {
+	_spec := &sqlgraph.QuerySpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   car.Table,
+			Columns: car.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: car.FieldID,
+			},
+		},
+		From:   cq.sql,
+		Unique: true,
+	}
+	if ps := cq.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if limit := cq.limit; limit != nil {
+		_spec.Limit = *limit
+	}
+	if offset := cq.offset; offset != nil {
+		_spec.Offset = *offset
+	}
+	if ps := cq.order; len(ps) > 0 {
+		_spec.Order = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return _spec
+}
+
+func (cq *CarQuery) sqlQuery() *sql.Selector {
+	builder := sql.Dialect(cq.driver.Dialect())
+	t1 := builder.Table(car.Table)
+	selector := builder.Select(t1.Columns(car.Columns...)...).From(t1)
+	if cq.sql != nil {
+		selector = cq.sql
+		selector.Select(selector.Columns(car.Columns...)...)
+	}
+	for _, p := range cq.predicates {
+		p(selector)
+	}
+	for _, p := range cq.order {
+		p(selector)
+	}
+	if offset := cq.offset; offset != nil {
+		// limit is mandatory for offset clause. We start
+		// with default value, and override it below if needed.
+		selector.Offset(*offset).Limit(math.MaxInt32)
+	}
+	if limit := cq.limit; limit != nil {
+		selector.Limit(*limit)
+	}
+	return selector
+}
+
+// CarGroupBy is the builder for group-by Car entities.
+type CarGroupBy struct {
+	config
+	fields []string
+	fns    []Aggregate
+	// intermediate query.
+	sql *sql.Selector
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (cgb *CarGroupBy) Aggregate(fns ...Aggregate) *CarGroupBy {
+	cgb.fns = append(cgb.fns, fns...)
+	return cgb
+}
+
+// Scan applies the group-by query and scan the result into the given value.
+func (cgb *CarGroupBy) Scan(ctx context.Context, v interface{}) error {
+	return cgb.sqlScan(ctx, v)
+}
+
+// ScanX is like Scan, but panics if an error occurs.
+func (cgb *CarGroupBy) ScanX(ctx context.Context, v interface{}) {
+	if err := cgb.Scan(ctx, v); err != nil {
+		panic(err)
+	}
+}
+
+// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+func (cgb *CarGroupBy) Strings(ctx context.Context) ([]string, error) {
+	if len(cgb.fields) > 1 {
+		return nil, errors.New("ent: CarGroupBy.Strings is not achievable when grouping more than 1 field")
+	}
+	var v []string
+	if err := cgb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// StringsX is like Strings, but panics if an error occurs.
+func (cgb *CarGroupBy) StringsX(ctx context.Context) []string {
+	v, err := cgb.Strings(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+func (cgb *CarGroupBy) Ints(ctx context.Context) ([]int, error) {
+	if len(cgb.fields) > 1 {
+		return nil, errors.New("ent: CarGroupBy.Ints is not achievable when grouping more than 1 field")
+	}
+	var v []int
+	if err := cgb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// IntsX is like Ints, but panics if an error occurs.
+func (cgb *CarGroupBy) IntsX(ctx context.Context) []int {
+	v, err := cgb.Ints(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+func (cgb *CarGroupBy) Float64s(ctx context.Context) ([]float64, error) {
+	if len(cgb.fields) > 1 {
+		return nil, errors.New("ent: CarGroupBy.Float64s is not achievable when grouping more than 1 field")
+	}
+	var v []float64
+	if err := cgb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// Float64sX is like Float64s, but panics if an error occurs.
+func (cgb *CarGroupBy) Float64sX(ctx context.Context) []float64 {
+	v, err := cgb.Float64s(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+func (cgb *CarGroupBy) Bools(ctx context.Context) ([]bool, error) {
+	if len(cgb.fields) > 1 {
+		return nil, errors.New("ent: CarGroupBy.Bools is not achievable when grouping more than 1 field")
+	}
+	var v []bool
+	if err := cgb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// BoolsX is like Bools, but panics if an error occurs.
+func (cgb *CarGroupBy) BoolsX(ctx context.Context) []bool {
+	v, err := cgb.Bools(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (cgb *CarGroupBy) sqlScan(ctx context.Context, v interface{}) error {
+	rows := &sql.Rows{}
+	query, args := cgb.sqlQuery().Query()
+	if err := cgb.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+func (cgb *CarGroupBy) sqlQuery() *sql.Selector {
+	selector := cgb.sql
+	columns := make([]string, 0, len(cgb.fields)+len(cgb.fns))
+	columns = append(columns, cgb.fields...)
+	for _, fn := range cgb.fns {
+		columns = append(columns, fn(selector))
+	}
+	return selector.Select(columns...).GroupBy(cgb.fields...)
+}
+
+// CarSelect is the builder for select fields of Car entities.
+type CarSelect struct {
+	config
+	fields []string
+	// intermediate queries.
+	sql *sql.Selector
+}
+
+// Scan applies the selector query and scan the result into the given value.
+func (cs *CarSelect) Scan(ctx context.Context, v interface{}) error {
+	return cs.sqlScan(ctx, v)
+}
+
+// ScanX is like Scan, but panics if an error occurs.
+func (cs *CarSelect) ScanX(ctx context.Context, v interface{}) {
+	if err := cs.Scan(ctx, v); err != nil {
+		panic(err)
+	}
+}
+
+// Strings returns list of strings from selector. It is only allowed when selecting one field.
+func (cs *CarSelect) Strings(ctx context.Context) ([]string, error) {
+	if len(cs.fields) > 1 {
+		return nil, errors.New("ent: CarSelect.Strings is not achievable when selecting more than 1 field")
+	}
+	var v []string
+	if err := cs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// StringsX is like Strings, but panics if an error occurs.
+func (cs *CarSelect) StringsX(ctx context.Context) []string {
+	v, err := cs.Strings(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Ints returns list of ints from selector. It is only allowed when selecting one field.
+func (cs *CarSelect) Ints(ctx context.Context) ([]int, error) {
+	if len(cs.fields) > 1 {
+		return nil, errors.New("ent: CarSelect.Ints is not achievable when selecting more than 1 field")
+	}
+	var v []int
+	if err := cs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// IntsX is like Ints, but panics if an error occurs.
+func (cs *CarSelect) IntsX(ctx context.Context) []int {
+	v, err := cs.Ints(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+func (cs *CarSelect) Float64s(ctx context.Context) ([]float64, error) {
+	if len(cs.fields) > 1 {
+		return nil, errors.New("ent: CarSelect.Float64s is not achievable when selecting more than 1 field")
+	}
+	var v []float64
+	if err := cs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// Float64sX is like Float64s, but panics if an error occurs.
+func (cs *CarSelect) Float64sX(ctx context.Context) []float64 {
+	v, err := cs.Float64s(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Bools returns list of bools from selector. It is only allowed when selecting one field.
+func (cs *CarSelect) Bools(ctx context.Context) ([]bool, error) {
+	if len(cs.fields) > 1 {
+		return nil, errors.New("ent: CarSelect.Bools is not achievable when selecting more than 1 field")
+	}
+	var v []bool
+	if err := cs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// BoolsX is like Bools, but panics if an error occurs.
+func (cs *CarSelect) BoolsX(ctx context.Context) []bool {
+	v, err := cs.Bools(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (cs *CarSelect) sqlScan(ctx context.Context, v interface{}) error {
+	rows := &sql.Rows{}
+	query, args := cs.sqlQuery().Query()
+	if err := cs.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+func (cs *CarSelect) sqlQuery() sql.Querier {
+	selector := cs.sql
+	selector.Select(selector.Columns(cs.fields...)...)
+	return selector
+}

+ 370 - 0
ent/car_update.go

@@ -0,0 +1,370 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// CarUpdate is the builder for updating Car entities.
+type CarUpdate struct {
+	config
+	hooks      []Hook
+	mutation   *CarMutation
+	predicates []predicate.Car
+}
+
+// Where adds a new predicate for the builder.
+func (cu *CarUpdate) Where(ps ...predicate.Car) *CarUpdate {
+	cu.predicates = append(cu.predicates, ps...)
+	return cu
+}
+
+// SetModel sets the model field.
+func (cu *CarUpdate) SetModel(s string) *CarUpdate {
+	cu.mutation.SetModel(s)
+	return cu
+}
+
+// SetRegisteredAt sets the registered_at field.
+func (cu *CarUpdate) SetRegisteredAt(t time.Time) *CarUpdate {
+	cu.mutation.SetRegisteredAt(t)
+	return cu
+}
+
+// SetOwnerID sets the owner edge to User by id.
+func (cu *CarUpdate) SetOwnerID(id int) *CarUpdate {
+	cu.mutation.SetOwnerID(id)
+	return cu
+}
+
+// SetNillableOwnerID sets the owner edge to User by id if the given value is not nil.
+func (cu *CarUpdate) SetNillableOwnerID(id *int) *CarUpdate {
+	if id != nil {
+		cu = cu.SetOwnerID(*id)
+	}
+	return cu
+}
+
+// SetOwner sets the owner edge to User.
+func (cu *CarUpdate) SetOwner(u *User) *CarUpdate {
+	return cu.SetOwnerID(u.ID)
+}
+
+// ClearOwner clears the owner edge to User.
+func (cu *CarUpdate) ClearOwner() *CarUpdate {
+	cu.mutation.ClearOwner()
+	return cu
+}
+
+// Save executes the query and returns the number of rows/vertices matched by this operation.
+func (cu *CarUpdate) Save(ctx context.Context) (int, error) {
+
+	var (
+		err      error
+		affected int
+	)
+	if len(cu.hooks) == 0 {
+		affected, err = cu.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*CarMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			cu.mutation = mutation
+			affected, err = cu.sqlSave(ctx)
+			return affected, err
+		})
+		for i := len(cu.hooks) - 1; i >= 0; i-- {
+			mut = cu.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, cu.mutation); err != nil {
+			return 0, err
+		}
+	}
+	return affected, err
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (cu *CarUpdate) SaveX(ctx context.Context) int {
+	affected, err := cu.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (cu *CarUpdate) Exec(ctx context.Context) error {
+	_, err := cu.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (cu *CarUpdate) ExecX(ctx context.Context) {
+	if err := cu.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+func (cu *CarUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	_spec := &sqlgraph.UpdateSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   car.Table,
+			Columns: car.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: car.FieldID,
+			},
+		},
+	}
+	if ps := cu.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := cu.mutation.Model(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: car.FieldModel,
+		})
+	}
+	if value, ok := cu.mutation.RegisteredAt(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeTime,
+			Value:  value,
+			Column: car.FieldRegisteredAt,
+		})
+	}
+	if cu.mutation.OwnerCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   car.OwnerTable,
+			Columns: []string{car.OwnerColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := cu.mutation.OwnerIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   car.OwnerTable,
+			Columns: []string{car.OwnerColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if n, err = sqlgraph.UpdateNodes(ctx, cu.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{car.Label}
+		} else if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return 0, err
+	}
+	return n, nil
+}
+
+// CarUpdateOne is the builder for updating a single Car entity.
+type CarUpdateOne struct {
+	config
+	hooks    []Hook
+	mutation *CarMutation
+}
+
+// SetModel sets the model field.
+func (cuo *CarUpdateOne) SetModel(s string) *CarUpdateOne {
+	cuo.mutation.SetModel(s)
+	return cuo
+}
+
+// SetRegisteredAt sets the registered_at field.
+func (cuo *CarUpdateOne) SetRegisteredAt(t time.Time) *CarUpdateOne {
+	cuo.mutation.SetRegisteredAt(t)
+	return cuo
+}
+
+// SetOwnerID sets the owner edge to User by id.
+func (cuo *CarUpdateOne) SetOwnerID(id int) *CarUpdateOne {
+	cuo.mutation.SetOwnerID(id)
+	return cuo
+}
+
+// SetNillableOwnerID sets the owner edge to User by id if the given value is not nil.
+func (cuo *CarUpdateOne) SetNillableOwnerID(id *int) *CarUpdateOne {
+	if id != nil {
+		cuo = cuo.SetOwnerID(*id)
+	}
+	return cuo
+}
+
+// SetOwner sets the owner edge to User.
+func (cuo *CarUpdateOne) SetOwner(u *User) *CarUpdateOne {
+	return cuo.SetOwnerID(u.ID)
+}
+
+// ClearOwner clears the owner edge to User.
+func (cuo *CarUpdateOne) ClearOwner() *CarUpdateOne {
+	cuo.mutation.ClearOwner()
+	return cuo
+}
+
+// Save executes the query and returns the updated entity.
+func (cuo *CarUpdateOne) Save(ctx context.Context) (*Car, error) {
+
+	var (
+		err  error
+		node *Car
+	)
+	if len(cuo.hooks) == 0 {
+		node, err = cuo.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*CarMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			cuo.mutation = mutation
+			node, err = cuo.sqlSave(ctx)
+			return node, err
+		})
+		for i := len(cuo.hooks) - 1; i >= 0; i-- {
+			mut = cuo.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, cuo.mutation); err != nil {
+			return nil, err
+		}
+	}
+	return node, err
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (cuo *CarUpdateOne) SaveX(ctx context.Context) *Car {
+	c, err := cuo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
+
+// Exec executes the query on the entity.
+func (cuo *CarUpdateOne) Exec(ctx context.Context) error {
+	_, err := cuo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (cuo *CarUpdateOne) ExecX(ctx context.Context) {
+	if err := cuo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+func (cuo *CarUpdateOne) sqlSave(ctx context.Context) (c *Car, err error) {
+	_spec := &sqlgraph.UpdateSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   car.Table,
+			Columns: car.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: car.FieldID,
+			},
+		},
+	}
+	id, ok := cuo.mutation.ID()
+	if !ok {
+		return nil, fmt.Errorf("missing Car.ID for update")
+	}
+	_spec.Node.ID.Value = id
+	if value, ok := cuo.mutation.Model(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: car.FieldModel,
+		})
+	}
+	if value, ok := cuo.mutation.RegisteredAt(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeTime,
+			Value:  value,
+			Column: car.FieldRegisteredAt,
+		})
+	}
+	if cuo.mutation.OwnerCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   car.OwnerTable,
+			Columns: []string{car.OwnerColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := cuo.mutation.OwnerIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   car.OwnerTable,
+			Columns: []string{car.OwnerColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	c = &Car{config: cuo.config}
+	_spec.Assign = c.assignValues
+	_spec.ScanValues = c.scanValues()
+	if err = sqlgraph.UpdateNode(ctx, cuo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{car.Label}
+		} else if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return nil, err
+	}
+	return c, nil
+}

+ 417 - 0
ent/client.go

@@ -0,0 +1,417 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+	"log"
+
+	"code.osinet.fr/fgm/entdemo/ent/migrate"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+
+	"github.com/facebookincubator/ent/dialect"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+)
+
+// Client is the client that holds all ent builders.
+type Client struct {
+	config
+	// Schema is the client for creating, migrating and dropping schema.
+	Schema *migrate.Schema
+	// Car is the client for interacting with the Car builders.
+	Car *CarClient
+	// Group is the client for interacting with the Group builders.
+	Group *GroupClient
+	// User is the client for interacting with the User builders.
+	User *UserClient
+}
+
+// NewClient creates a new client configured with the given options.
+func NewClient(opts ...Option) *Client {
+	cfg := config{log: log.Println, hooks: &hooks{}}
+	cfg.options(opts...)
+	client := &Client{config: cfg}
+	client.init()
+	return client
+}
+
+func (c *Client) init() {
+	c.Schema = migrate.NewSchema(c.driver)
+	c.Car = NewCarClient(c.config)
+	c.Group = NewGroupClient(c.config)
+	c.User = NewUserClient(c.config)
+}
+
+// Open opens a connection to the database specified by the driver name and a
+// driver-specific data source name, and returns a new client attached to it.
+// Optional parameters can be added for configuring the client.
+func Open(driverName, dataSourceName string, options ...Option) (*Client, error) {
+	switch driverName {
+	case dialect.MySQL, dialect.Postgres, dialect.SQLite:
+		drv, err := sql.Open(driverName, dataSourceName)
+		if err != nil {
+			return nil, err
+		}
+		return NewClient(append(options, Driver(drv))...), nil
+	default:
+		return nil, fmt.Errorf("unsupported driver: %q", driverName)
+	}
+}
+
+// Tx returns a new transactional client.
+func (c *Client) Tx(ctx context.Context) (*Tx, error) {
+	if _, ok := c.driver.(*txDriver); ok {
+		return nil, fmt.Errorf("ent: cannot start a transaction within a transaction")
+	}
+	tx, err := newTx(ctx, c.driver)
+	if err != nil {
+		return nil, fmt.Errorf("ent: starting a transaction: %v", err)
+	}
+	cfg := config{driver: tx, log: c.log, debug: c.debug, hooks: c.hooks}
+	return &Tx{
+		config: cfg,
+		Car:    NewCarClient(cfg),
+		Group:  NewGroupClient(cfg),
+		User:   NewUserClient(cfg),
+	}, nil
+}
+
+// Debug returns a new debug-client. It's used to get verbose logging on specific operations.
+//
+//	client.Debug().
+//		Car.
+//		Query().
+//		Count(ctx)
+//
+func (c *Client) Debug() *Client {
+	if c.debug {
+		return c
+	}
+	cfg := config{driver: dialect.Debug(c.driver, c.log), log: c.log, debug: true, hooks: c.hooks}
+	client := &Client{config: cfg}
+	client.init()
+	return client
+}
+
+// Close closes the database connection and prevents new queries from starting.
+func (c *Client) Close() error {
+	return c.driver.Close()
+}
+
+// Use adds the mutation hooks to all the entity clients.
+// In order to add hooks to a specific client, call: `client.Node.Use(...)`.
+func (c *Client) Use(hooks ...Hook) {
+	c.Car.Use(hooks...)
+	c.Group.Use(hooks...)
+	c.User.Use(hooks...)
+}
+
+// CarClient is a client for the Car schema.
+type CarClient struct {
+	config
+}
+
+// NewCarClient returns a client for the Car from the given config.
+func NewCarClient(c config) *CarClient {
+	return &CarClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `car.Hooks(f(g(h())))`.
+func (c *CarClient) Use(hooks ...Hook) {
+	c.hooks.Car = append(c.hooks.Car, hooks...)
+}
+
+// Create returns a create builder for Car.
+func (c *CarClient) Create() *CarCreate {
+	mutation := newCarMutation(c.config, OpCreate)
+	return &CarCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Update returns an update builder for Car.
+func (c *CarClient) Update() *CarUpdate {
+	mutation := newCarMutation(c.config, OpUpdate)
+	return &CarUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *CarClient) UpdateOne(ca *Car) *CarUpdateOne {
+	return c.UpdateOneID(ca.ID)
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *CarClient) UpdateOneID(id int) *CarUpdateOne {
+	mutation := newCarMutation(c.config, OpUpdateOne)
+	mutation.id = &id
+	return &CarUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for Car.
+func (c *CarClient) Delete() *CarDelete {
+	mutation := newCarMutation(c.config, OpDelete)
+	return &CarDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a delete builder for the given entity.
+func (c *CarClient) DeleteOne(ca *Car) *CarDeleteOne {
+	return c.DeleteOneID(ca.ID)
+}
+
+// DeleteOneID returns a delete builder for the given id.
+func (c *CarClient) DeleteOneID(id int) *CarDeleteOne {
+	builder := c.Delete().Where(car.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &CarDeleteOne{builder}
+}
+
+// Create returns a query builder for Car.
+func (c *CarClient) Query() *CarQuery {
+	return &CarQuery{config: c.config}
+}
+
+// Get returns a Car entity by its id.
+func (c *CarClient) Get(ctx context.Context, id int) (*Car, error) {
+	return c.Query().Where(car.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *CarClient) GetX(ctx context.Context, id int) *Car {
+	ca, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return ca
+}
+
+// QueryOwner queries the owner edge of a Car.
+func (c *CarClient) QueryOwner(ca *Car) *UserQuery {
+	query := &UserQuery{config: c.config}
+	id := ca.ID
+	step := sqlgraph.NewStep(
+		sqlgraph.From(car.Table, car.FieldID, id),
+		sqlgraph.To(user.Table, user.FieldID),
+		sqlgraph.Edge(sqlgraph.M2O, true, car.OwnerTable, car.OwnerColumn),
+	)
+	query.sql = sqlgraph.Neighbors(ca.driver.Dialect(), step)
+
+	return query
+}
+
+// Hooks returns the client hooks.
+func (c *CarClient) Hooks() []Hook {
+	return c.hooks.Car
+}
+
+// GroupClient is a client for the Group schema.
+type GroupClient struct {
+	config
+}
+
+// NewGroupClient returns a client for the Group from the given config.
+func NewGroupClient(c config) *GroupClient {
+	return &GroupClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `group.Hooks(f(g(h())))`.
+func (c *GroupClient) Use(hooks ...Hook) {
+	c.hooks.Group = append(c.hooks.Group, hooks...)
+}
+
+// Create returns a create builder for Group.
+func (c *GroupClient) Create() *GroupCreate {
+	mutation := newGroupMutation(c.config, OpCreate)
+	return &GroupCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Update returns an update builder for Group.
+func (c *GroupClient) Update() *GroupUpdate {
+	mutation := newGroupMutation(c.config, OpUpdate)
+	return &GroupUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *GroupClient) UpdateOne(gr *Group) *GroupUpdateOne {
+	return c.UpdateOneID(gr.ID)
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *GroupClient) UpdateOneID(id int) *GroupUpdateOne {
+	mutation := newGroupMutation(c.config, OpUpdateOne)
+	mutation.id = &id
+	return &GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for Group.
+func (c *GroupClient) Delete() *GroupDelete {
+	mutation := newGroupMutation(c.config, OpDelete)
+	return &GroupDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a delete builder for the given entity.
+func (c *GroupClient) DeleteOne(gr *Group) *GroupDeleteOne {
+	return c.DeleteOneID(gr.ID)
+}
+
+// DeleteOneID returns a delete builder for the given id.
+func (c *GroupClient) DeleteOneID(id int) *GroupDeleteOne {
+	builder := c.Delete().Where(group.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &GroupDeleteOne{builder}
+}
+
+// Create returns a query builder for Group.
+func (c *GroupClient) Query() *GroupQuery {
+	return &GroupQuery{config: c.config}
+}
+
+// Get returns a Group entity by its id.
+func (c *GroupClient) Get(ctx context.Context, id int) (*Group, error) {
+	return c.Query().Where(group.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *GroupClient) GetX(ctx context.Context, id int) *Group {
+	gr, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return gr
+}
+
+// QueryUsers queries the users edge of a Group.
+func (c *GroupClient) QueryUsers(gr *Group) *UserQuery {
+	query := &UserQuery{config: c.config}
+	id := gr.ID
+	step := sqlgraph.NewStep(
+		sqlgraph.From(group.Table, group.FieldID, id),
+		sqlgraph.To(user.Table, user.FieldID),
+		sqlgraph.Edge(sqlgraph.M2M, false, group.UsersTable, group.UsersPrimaryKey...),
+	)
+	query.sql = sqlgraph.Neighbors(gr.driver.Dialect(), step)
+
+	return query
+}
+
+// Hooks returns the client hooks.
+func (c *GroupClient) Hooks() []Hook {
+	return c.hooks.Group
+}
+
+// UserClient is a client for the User schema.
+type UserClient struct {
+	config
+}
+
+// NewUserClient returns a client for the User from the given config.
+func NewUserClient(c config) *UserClient {
+	return &UserClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `user.Hooks(f(g(h())))`.
+func (c *UserClient) Use(hooks ...Hook) {
+	c.hooks.User = append(c.hooks.User, hooks...)
+}
+
+// Create returns a create builder for User.
+func (c *UserClient) Create() *UserCreate {
+	mutation := newUserMutation(c.config, OpCreate)
+	return &UserCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Update returns an update builder for User.
+func (c *UserClient) Update() *UserUpdate {
+	mutation := newUserMutation(c.config, OpUpdate)
+	return &UserUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *UserClient) UpdateOne(u *User) *UserUpdateOne {
+	return c.UpdateOneID(u.ID)
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *UserClient) UpdateOneID(id int) *UserUpdateOne {
+	mutation := newUserMutation(c.config, OpUpdateOne)
+	mutation.id = &id
+	return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for User.
+func (c *UserClient) Delete() *UserDelete {
+	mutation := newUserMutation(c.config, OpDelete)
+	return &UserDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a delete builder for the given entity.
+func (c *UserClient) DeleteOne(u *User) *UserDeleteOne {
+	return c.DeleteOneID(u.ID)
+}
+
+// DeleteOneID returns a delete builder for the given id.
+func (c *UserClient) DeleteOneID(id int) *UserDeleteOne {
+	builder := c.Delete().Where(user.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &UserDeleteOne{builder}
+}
+
+// Create returns a query builder for User.
+func (c *UserClient) Query() *UserQuery {
+	return &UserQuery{config: c.config}
+}
+
+// Get returns a User entity by its id.
+func (c *UserClient) Get(ctx context.Context, id int) (*User, error) {
+	return c.Query().Where(user.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *UserClient) GetX(ctx context.Context, id int) *User {
+	u, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return u
+}
+
+// QueryCars queries the cars edge of a User.
+func (c *UserClient) QueryCars(u *User) *CarQuery {
+	query := &CarQuery{config: c.config}
+	id := u.ID
+	step := sqlgraph.NewStep(
+		sqlgraph.From(user.Table, user.FieldID, id),
+		sqlgraph.To(car.Table, car.FieldID),
+		sqlgraph.Edge(sqlgraph.O2M, false, user.CarsTable, user.CarsColumn),
+	)
+	query.sql = sqlgraph.Neighbors(u.driver.Dialect(), step)
+
+	return query
+}
+
+// QueryGroups queries the groups edge of a User.
+func (c *UserClient) QueryGroups(u *User) *GroupQuery {
+	query := &GroupQuery{config: c.config}
+	id := u.ID
+	step := sqlgraph.NewStep(
+		sqlgraph.From(user.Table, user.FieldID, id),
+		sqlgraph.To(group.Table, group.FieldID),
+		sqlgraph.Edge(sqlgraph.M2M, true, user.GroupsTable, user.GroupsPrimaryKey...),
+	)
+	query.sql = sqlgraph.Neighbors(u.driver.Dialect(), step)
+
+	return query
+}
+
+// Hooks returns the client hooks.
+func (c *UserClient) Hooks() []Hook {
+	return c.hooks.User
+}

+ 61 - 0
ent/config.go

@@ -0,0 +1,61 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"github.com/facebookincubator/ent"
+	"github.com/facebookincubator/ent/dialect"
+)
+
+// Option function to configure the client.
+type Option func(*config)
+
+// Config is the configuration for the client and its builder.
+type config struct {
+	// driver used for executing database requests.
+	driver dialect.Driver
+	// debug enable a debug logging.
+	debug bool
+	// log used for logging on debug mode.
+	log func(...interface{})
+	// hooks to execute on mutations.
+	hooks *hooks
+}
+
+// hooks per client, for fast access.
+type hooks struct {
+	Car   []ent.Hook
+	Group []ent.Hook
+	User  []ent.Hook
+}
+
+// Options applies the options on the config object.
+func (c *config) options(opts ...Option) {
+	for _, opt := range opts {
+		opt(c)
+	}
+	if c.debug {
+		c.driver = dialect.Debug(c.driver, c.log)
+	}
+}
+
+// Debug enables debug logging on the ent.Driver.
+func Debug() Option {
+	return func(c *config) {
+		c.debug = true
+	}
+}
+
+// Log sets the logging function for debug mode.
+func Log(fn func(...interface{})) Option {
+	return func(c *config) {
+		c.log = fn
+	}
+}
+
+// Driver configures the client driver.
+func Driver(driver dialect.Driver) Option {
+	return func(c *config) {
+		c.driver = driver
+	}
+}

+ 20 - 0
ent/context.go

@@ -0,0 +1,20 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+)
+
+type contextKey struct{}
+
+// FromContext returns the Client stored in a context, or nil if there isn't one.
+func FromContext(ctx context.Context) *Client {
+	c, _ := ctx.Value(contextKey{}).(*Client)
+	return c
+}
+
+// NewContext returns a new context with the given Client attached.
+func NewContext(parent context.Context, c *Client) context.Context {
+	return context.WithValue(parent, contextKey{}, c)
+}

+ 262 - 0
ent/ent.go

@@ -0,0 +1,262 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/facebookincubator/ent"
+	"github.com/facebookincubator/ent/dialect"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"golang.org/x/xerrors"
+)
+
+// ent aliases to avoid import conflict in user's code.
+type (
+	Op         = ent.Op
+	Hook       = ent.Hook
+	Value      = ent.Value
+	Query      = ent.Query
+	Mutator    = ent.Mutator
+	Mutation   = ent.Mutation
+	MutateFunc = ent.MutateFunc
+)
+
+// Order applies an ordering on either graph traversal or sql selector.
+type Order func(*sql.Selector)
+
+// Asc applies the given fields in ASC order.
+func Asc(fields ...string) Order {
+	return func(s *sql.Selector) {
+		for _, f := range fields {
+			s.OrderBy(sql.Asc(f))
+		}
+	}
+}
+
+// Desc applies the given fields in DESC order.
+func Desc(fields ...string) Order {
+	return func(s *sql.Selector) {
+		for _, f := range fields {
+			s.OrderBy(sql.Desc(f))
+		}
+	}
+}
+
+// Aggregate applies an aggregation step on the group-by traversal/selector.
+type Aggregate func(*sql.Selector) string
+
+// As is a pseudo aggregation function for renaming another other functions with custom names. For example:
+//
+//	GroupBy(field1, field2).
+//	Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")).
+//	Scan(ctx, &v)
+//
+func As(fn Aggregate, end string) Aggregate {
+	return func(s *sql.Selector) string {
+		return sql.As(fn(s), end)
+	}
+}
+
+// Count applies the "count" aggregation function on each group.
+func Count() Aggregate {
+	return func(s *sql.Selector) string {
+		return sql.Count("*")
+	}
+}
+
+// Max applies the "max" aggregation function on the given field of each group.
+func Max(field string) Aggregate {
+	return func(s *sql.Selector) string {
+		return sql.Max(s.C(field))
+	}
+}
+
+// Mean applies the "mean" aggregation function on the given field of each group.
+func Mean(field string) Aggregate {
+	return func(s *sql.Selector) string {
+		return sql.Avg(s.C(field))
+	}
+}
+
+// Min applies the "min" aggregation function on the given field of each group.
+func Min(field string) Aggregate {
+	return func(s *sql.Selector) string {
+		return sql.Min(s.C(field))
+	}
+}
+
+// Sum applies the "sum" aggregation function on the given field of each group.
+func Sum(field string) Aggregate {
+	return func(s *sql.Selector) string {
+		return sql.Sum(s.C(field))
+	}
+}
+
+// NotFoundError returns when trying to fetch a specific entity and it was not found in the database.
+type NotFoundError struct {
+	label string
+}
+
+// Error implements the error interface.
+func (e *NotFoundError) Error() string {
+	return "ent: " + e.label + " not found"
+}
+
+// IsNotFound returns a boolean indicating whether the error is a not found error.
+func IsNotFound(err error) bool {
+	if err == nil {
+		return false
+	}
+	var e *NotFoundError
+	return xerrors.As(err, &e)
+}
+
+// MaskNotFound masks nor found error.
+func MaskNotFound(err error) error {
+	if IsNotFound(err) {
+		return nil
+	}
+	return err
+}
+
+// NotSingularError returns when trying to fetch a singular entity and more then one was found in the database.
+type NotSingularError struct {
+	label string
+}
+
+// Error implements the error interface.
+func (e *NotSingularError) Error() string {
+	return "ent: " + e.label + " not singular"
+}
+
+// IsNotSingular returns a boolean indicating whether the error is a not singular error.
+func IsNotSingular(err error) bool {
+	if err == nil {
+		return false
+	}
+	var e *NotSingularError
+	return xerrors.As(err, &e)
+}
+
+// NotLoadedError returns when trying to get a node that was not loaded by the query.
+type NotLoadedError struct {
+	edge string
+}
+
+// Error implements the error interface.
+func (e *NotLoadedError) Error() string {
+	return "ent: " + e.edge + " edge was not loaded"
+}
+
+// IsNotLoaded returns a boolean indicating whether the error is a not loaded error.
+func IsNotLoaded(err error) bool {
+	if err == nil {
+		return false
+	}
+	var e *NotLoadedError
+	return xerrors.As(err, &e)
+}
+
+// ConstraintError returns when trying to create/update one or more entities and
+// one or more of their constraints failed. For example, violation of edge or
+// field uniqueness.
+type ConstraintError struct {
+	msg  string
+	wrap error
+}
+
+// Error implements the error interface.
+func (e ConstraintError) Error() string {
+	return "ent: constraint failed: " + e.msg
+}
+
+// Unwrap implements the errors.Wrapper interface.
+func (e *ConstraintError) Unwrap() error {
+	return e.wrap
+}
+
+// IsConstraintError returns a boolean indicating whether the error is a constraint failure.
+func IsConstraintError(err error) bool {
+	if err == nil {
+		return false
+	}
+	var e *ConstraintError
+	return xerrors.As(err, &e)
+}
+
+func isSQLConstraintError(err error) (*ConstraintError, bool) {
+	var (
+		msg = err.Error()
+		// error format per dialect.
+		errors = [...]string{
+			"Error 1062",               // MySQL 1062 error (ER_DUP_ENTRY).
+			"UNIQUE constraint failed", // SQLite.
+			"duplicate key value violates unique constraint", // PostgreSQL.
+		}
+	)
+	if _, ok := err.(*sqlgraph.ConstraintError); ok {
+		return &ConstraintError{msg, err}, true
+	}
+	for i := range errors {
+		if strings.Contains(msg, errors[i]) {
+			return &ConstraintError{msg, err}, true
+		}
+	}
+	return nil, false
+}
+
+// rollback calls to tx.Rollback and wraps the given error with the rollback error if occurred.
+func rollback(tx dialect.Tx, err error) error {
+	if rerr := tx.Rollback(); rerr != nil {
+		err = fmt.Errorf("%s: %v", err.Error(), rerr)
+	}
+	if err, ok := isSQLConstraintError(err); ok {
+		return err
+	}
+	return err
+}
+
+// insertLastID invokes the insert query on the transaction and returns the LastInsertID.
+func insertLastID(ctx context.Context, tx dialect.Tx, insert *sql.InsertBuilder) (int64, error) {
+	query, args := insert.Query()
+	// PostgreSQL does not support the LastInsertId() method of sql.Result
+	// on Exec, and should be extracted manually using the `RETURNING` clause.
+	if insert.Dialect() == dialect.Postgres {
+		rows := &sql.Rows{}
+		if err := tx.Query(ctx, query, args, rows); err != nil {
+			return 0, err
+		}
+		defer rows.Close()
+		if !rows.Next() {
+			return 0, fmt.Errorf("no rows found for query: %v", query)
+		}
+		var id int64
+		if err := rows.Scan(&id); err != nil {
+			return 0, err
+		}
+		return id, nil
+	}
+	// MySQL, SQLite, etc.
+	var res sql.Result
+	if err := tx.Exec(ctx, query, args, &res); err != nil {
+		return 0, err
+	}
+	id, err := res.LastInsertId()
+	if err != nil {
+		return 0, err
+	}
+	return id, nil
+}
+
+// keys returns the keys/ids from the edge map.
+func keys(m map[int]struct{}) []int {
+	s := make([]int, 0, len(m))
+	for id := range m {
+		s = append(s, id)
+	}
+	return s
+}

+ 3 - 0
ent/generate.go

@@ -0,0 +1,3 @@
+package ent
+
+//go:generate go run github.com/facebookincubator/ent/cmd/entc generate ./schema

+ 112 - 0
ent/group.go

@@ -0,0 +1,112 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"fmt"
+	"strings"
+
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"github.com/facebookincubator/ent/dialect/sql"
+)
+
+// Group is the model entity for the Group schema.
+type Group struct {
+	config `json:"-"`
+	// ID of the ent.
+	ID int `json:"id,omitempty"`
+	// Name holds the value of the "name" field.
+	Name string `json:"name,omitempty"`
+	// Edges holds the relations/edges for other nodes in the graph.
+	// The values are being populated by the GroupQuery when eager-loading is set.
+	Edges GroupEdges `json:"edges"`
+}
+
+// GroupEdges holds the relations/edges for other nodes in the graph.
+type GroupEdges struct {
+	// Users holds the value of the users edge.
+	Users []*User
+	// loadedTypes holds the information for reporting if a
+	// type was loaded (or requested) in eager-loading or not.
+	loadedTypes [1]bool
+}
+
+// UsersOrErr returns the Users value or an error if the edge
+// was not loaded in eager-loading.
+func (e GroupEdges) UsersOrErr() ([]*User, error) {
+	if e.loadedTypes[0] {
+		return e.Users, nil
+	}
+	return nil, &NotLoadedError{edge: "users"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*Group) scanValues() []interface{} {
+	return []interface{}{
+		&sql.NullInt64{},  // id
+		&sql.NullString{}, // name
+	}
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the Group fields.
+func (gr *Group) assignValues(values ...interface{}) error {
+	if m, n := len(values), len(group.Columns); m < n {
+		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+	}
+	value, ok := values[0].(*sql.NullInt64)
+	if !ok {
+		return fmt.Errorf("unexpected type %T for field id", value)
+	}
+	gr.ID = int(value.Int64)
+	values = values[1:]
+	if value, ok := values[0].(*sql.NullString); !ok {
+		return fmt.Errorf("unexpected type %T for field name", values[0])
+	} else if value.Valid {
+		gr.Name = value.String
+	}
+	return nil
+}
+
+// QueryUsers queries the users edge of the Group.
+func (gr *Group) QueryUsers() *UserQuery {
+	return (&GroupClient{config: gr.config}).QueryUsers(gr)
+}
+
+// Update returns a builder for updating this Group.
+// Note that, you need to call Group.Unwrap() before calling this method, if this Group
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (gr *Group) Update() *GroupUpdateOne {
+	return (&GroupClient{config: gr.config}).UpdateOne(gr)
+}
+
+// Unwrap unwraps the entity that was returned from a transaction after it was closed,
+// so that all next queries will be executed through the driver which created the transaction.
+func (gr *Group) Unwrap() *Group {
+	tx, ok := gr.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: Group is not a transactional entity")
+	}
+	gr.config.driver = tx.drv
+	return gr
+}
+
+// String implements the fmt.Stringer.
+func (gr *Group) String() string {
+	var builder strings.Builder
+	builder.WriteString("Group(")
+	builder.WriteString(fmt.Sprintf("id=%v", gr.ID))
+	builder.WriteString(", name=")
+	builder.WriteString(gr.Name)
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// Groups is a parsable slice of Group.
+type Groups []*Group
+
+func (gr Groups) config(cfg config) {
+	for _i := range gr {
+		gr[_i].config = cfg
+	}
+}

+ 39 - 0
ent/group/group.go

@@ -0,0 +1,39 @@
+// Code generated by entc, DO NOT EDIT.
+
+package group
+
+const (
+	// Label holds the string label denoting the group type in the database.
+	Label = "group"
+	// FieldID holds the string denoting the id field in the database.
+	FieldID   = "id" // FieldName holds the string denoting the name vertex property in the database.
+	FieldName = "name"
+
+	// EdgeUsers holds the string denoting the users edge name in mutations.
+	EdgeUsers = "users"
+
+	// Table holds the table name of the group in the database.
+	Table = "groups"
+	// UsersTable is the table the holds the users relation/edge. The primary key declared below.
+	UsersTable = "group_users"
+	// UsersInverseTable is the table name for the User entity.
+	// It exists in this package in order to avoid circular dependency with the "user" package.
+	UsersInverseTable = "users"
+)
+
+// Columns holds all SQL columns for group fields.
+var Columns = []string{
+	FieldID,
+	FieldName,
+}
+
+var (
+	// UsersPrimaryKey and UsersColumn2 are the table columns denoting the
+	// primary key for the users relation (M2M).
+	UsersPrimaryKey = []string{"group_id", "user_id"}
+)
+
+var (
+	// NameValidator is a validator for the "name" field. It is called by the builders before save.
+	NameValidator func(string) error
+)

+ 270 - 0
ent/group/where.go

@@ -0,0 +1,270 @@
+// Code generated by entc, DO NOT EDIT.
+
+package group
+
+import (
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+)
+
+// ID filters vertices based on their identifier.
+func ID(id int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldID), id))
+	})
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldID), id))
+	})
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldID), id))
+	})
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(ids) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		v := make([]interface{}, len(ids))
+		for i := range v {
+			v[i] = ids[i]
+		}
+		s.Where(sql.In(s.C(FieldID), v...))
+	})
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(ids) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		v := make([]interface{}, len(ids))
+		for i := range v {
+			v[i] = ids[i]
+		}
+		s.Where(sql.NotIn(s.C(FieldID), v...))
+	})
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldID), id))
+	})
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldID), id))
+	})
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldID), id))
+	})
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id int) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldID), id))
+	})
+}
+
+// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
+func Name(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldName), v))
+	})
+}
+
+// NameEQ applies the EQ predicate on the "name" field.
+func NameEQ(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldName), v))
+	})
+}
+
+// NameNEQ applies the NEQ predicate on the "name" field.
+func NameNEQ(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldName), v))
+	})
+}
+
+// NameIn applies the In predicate on the "name" field.
+func NameIn(vs ...string) predicate.Group {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Group(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldName), v...))
+	})
+}
+
+// NameNotIn applies the NotIn predicate on the "name" field.
+func NameNotIn(vs ...string) predicate.Group {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.Group(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldName), v...))
+	})
+}
+
+// NameGT applies the GT predicate on the "name" field.
+func NameGT(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldName), v))
+	})
+}
+
+// NameGTE applies the GTE predicate on the "name" field.
+func NameGTE(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldName), v))
+	})
+}
+
+// NameLT applies the LT predicate on the "name" field.
+func NameLT(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldName), v))
+	})
+}
+
+// NameLTE applies the LTE predicate on the "name" field.
+func NameLTE(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldName), v))
+	})
+}
+
+// NameContains applies the Contains predicate on the "name" field.
+func NameContains(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.Contains(s.C(FieldName), v))
+	})
+}
+
+// NameHasPrefix applies the HasPrefix predicate on the "name" field.
+func NameHasPrefix(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.HasPrefix(s.C(FieldName), v))
+	})
+}
+
+// NameHasSuffix applies the HasSuffix predicate on the "name" field.
+func NameHasSuffix(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.HasSuffix(s.C(FieldName), v))
+	})
+}
+
+// NameEqualFold applies the EqualFold predicate on the "name" field.
+func NameEqualFold(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.EqualFold(s.C(FieldName), v))
+	})
+}
+
+// NameContainsFold applies the ContainsFold predicate on the "name" field.
+func NameContainsFold(v string) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s.Where(sql.ContainsFold(s.C(FieldName), v))
+	})
+}
+
+// HasUsers applies the HasEdge predicate on the "users" edge.
+func HasUsers() predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(UsersTable, FieldID),
+			sqlgraph.Edge(sqlgraph.M2M, false, UsersTable, UsersPrimaryKey...),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasUsersWith applies the HasEdge predicate on the "users" edge with a given conditions (other predicates).
+func HasUsersWith(preds ...predicate.User) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(UsersInverseTable, FieldID),
+			sqlgraph.Edge(sqlgraph.M2M, false, UsersTable, UsersPrimaryKey...),
+		)
+		sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+			for _, p := range preds {
+				p(s)
+			}
+		})
+	})
+}
+
+// And groups list of predicates with the AND operator between them.
+func And(predicates ...predicate.Group) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s1 := s.Clone().SetP(nil)
+		for _, p := range predicates {
+			p(s1)
+		}
+		s.Where(s1.P())
+	})
+}
+
+// Or groups list of predicates with the OR operator between them.
+func Or(predicates ...predicate.Group) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		s1 := s.Clone().SetP(nil)
+		for i, p := range predicates {
+			if i > 0 {
+				s1.Or()
+			}
+			p(s1)
+		}
+		s.Where(s1.P())
+	})
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.Group) predicate.Group {
+	return predicate.Group(func(s *sql.Selector) {
+		p(s.Not())
+	})
+}

+ 136 - 0
ent/group_create.go

@@ -0,0 +1,136 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// GroupCreate is the builder for creating a Group entity.
+type GroupCreate struct {
+	config
+	mutation *GroupMutation
+	hooks    []Hook
+}
+
+// SetName sets the name field.
+func (gc *GroupCreate) SetName(s string) *GroupCreate {
+	gc.mutation.SetName(s)
+	return gc
+}
+
+// AddUserIDs adds the users edge to User by ids.
+func (gc *GroupCreate) AddUserIDs(ids ...int) *GroupCreate {
+	gc.mutation.AddUserIDs(ids...)
+	return gc
+}
+
+// AddUsers adds the users edges to User.
+func (gc *GroupCreate) AddUsers(u ...*User) *GroupCreate {
+	ids := make([]int, len(u))
+	for i := range u {
+		ids[i] = u[i].ID
+	}
+	return gc.AddUserIDs(ids...)
+}
+
+// Save creates the Group in the database.
+func (gc *GroupCreate) Save(ctx context.Context) (*Group, error) {
+	if _, ok := gc.mutation.Name(); !ok {
+		return nil, errors.New("ent: missing required field \"name\"")
+	}
+	if v, ok := gc.mutation.Name(); ok {
+		if err := group.NameValidator(v); err != nil {
+			return nil, fmt.Errorf("ent: validator failed for field \"name\": %v", err)
+		}
+	}
+	var (
+		err  error
+		node *Group
+	)
+	if len(gc.hooks) == 0 {
+		node, err = gc.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*GroupMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			gc.mutation = mutation
+			node, err = gc.sqlSave(ctx)
+			return node, err
+		})
+		for i := len(gc.hooks) - 1; i >= 0; i-- {
+			mut = gc.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, gc.mutation); err != nil {
+			return nil, err
+		}
+	}
+	return node, err
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (gc *GroupCreate) SaveX(ctx context.Context) *Group {
+	v, err := gc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (gc *GroupCreate) sqlSave(ctx context.Context) (*Group, error) {
+	var (
+		gr    = &Group{config: gc.config}
+		_spec = &sqlgraph.CreateSpec{
+			Table: group.Table,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: group.FieldID,
+			},
+		}
+	)
+	if value, ok := gc.mutation.Name(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: group.FieldName,
+		})
+		gr.Name = value
+	}
+	if nodes := gc.mutation.UsersIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: false,
+			Table:   group.UsersTable,
+			Columns: group.UsersPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges = append(_spec.Edges, edge)
+	}
+	if err := sqlgraph.CreateNode(ctx, gc.driver, _spec); err != nil {
+		if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return nil, err
+	}
+	id := _spec.ID.Value.(int64)
+	gr.ID = int(id)
+	return gr, nil
+}

+ 108 - 0
ent/group_delete.go

@@ -0,0 +1,108 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// GroupDelete is the builder for deleting a Group entity.
+type GroupDelete struct {
+	config
+	hooks      []Hook
+	mutation   *GroupMutation
+	predicates []predicate.Group
+}
+
+// Where adds a new predicate to the delete builder.
+func (gd *GroupDelete) Where(ps ...predicate.Group) *GroupDelete {
+	gd.predicates = append(gd.predicates, ps...)
+	return gd
+}
+
+// Exec executes the deletion query and returns how many vertices were deleted.
+func (gd *GroupDelete) Exec(ctx context.Context) (int, error) {
+	var (
+		err      error
+		affected int
+	)
+	if len(gd.hooks) == 0 {
+		affected, err = gd.sqlExec(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*GroupMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			gd.mutation = mutation
+			affected, err = gd.sqlExec(ctx)
+			return affected, err
+		})
+		for i := len(gd.hooks) - 1; i >= 0; i-- {
+			mut = gd.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, gd.mutation); err != nil {
+			return 0, err
+		}
+	}
+	return affected, err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (gd *GroupDelete) ExecX(ctx context.Context) int {
+	n, err := gd.Exec(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return n
+}
+
+func (gd *GroupDelete) sqlExec(ctx context.Context) (int, error) {
+	_spec := &sqlgraph.DeleteSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table: group.Table,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: group.FieldID,
+			},
+		},
+	}
+	if ps := gd.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return sqlgraph.DeleteNodes(ctx, gd.driver, _spec)
+}
+
+// GroupDeleteOne is the builder for deleting a single Group entity.
+type GroupDeleteOne struct {
+	gd *GroupDelete
+}
+
+// Exec executes the deletion query.
+func (gdo *GroupDeleteOne) Exec(ctx context.Context) error {
+	n, err := gdo.gd.Exec(ctx)
+	switch {
+	case err != nil:
+		return err
+	case n == 0:
+		return &NotFoundError{group.Label}
+	default:
+		return nil
+	}
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (gdo *GroupDeleteOne) ExecX(ctx context.Context) {
+	gdo.gd.ExecX(ctx)
+}

+ 709 - 0
ent/group_query.go

@@ -0,0 +1,709 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"database/sql/driver"
+	"errors"
+	"fmt"
+	"math"
+
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// GroupQuery is the builder for querying Group entities.
+type GroupQuery struct {
+	config
+	limit      *int
+	offset     *int
+	order      []Order
+	unique     []string
+	predicates []predicate.Group
+	// eager-loading edges.
+	withUsers *UserQuery
+	// intermediate query.
+	sql *sql.Selector
+}
+
+// Where adds a new predicate for the builder.
+func (gq *GroupQuery) Where(ps ...predicate.Group) *GroupQuery {
+	gq.predicates = append(gq.predicates, ps...)
+	return gq
+}
+
+// Limit adds a limit step to the query.
+func (gq *GroupQuery) Limit(limit int) *GroupQuery {
+	gq.limit = &limit
+	return gq
+}
+
+// Offset adds an offset step to the query.
+func (gq *GroupQuery) Offset(offset int) *GroupQuery {
+	gq.offset = &offset
+	return gq
+}
+
+// Order adds an order step to the query.
+func (gq *GroupQuery) Order(o ...Order) *GroupQuery {
+	gq.order = append(gq.order, o...)
+	return gq
+}
+
+// QueryUsers chains the current query on the users edge.
+func (gq *GroupQuery) QueryUsers() *UserQuery {
+	query := &UserQuery{config: gq.config}
+	step := sqlgraph.NewStep(
+		sqlgraph.From(group.Table, group.FieldID, gq.sqlQuery()),
+		sqlgraph.To(user.Table, user.FieldID),
+		sqlgraph.Edge(sqlgraph.M2M, false, group.UsersTable, group.UsersPrimaryKey...),
+	)
+	query.sql = sqlgraph.SetNeighbors(gq.driver.Dialect(), step)
+	return query
+}
+
+// First returns the first Group entity in the query. Returns *NotFoundError when no group was found.
+func (gq *GroupQuery) First(ctx context.Context) (*Group, error) {
+	grs, err := gq.Limit(1).All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	if len(grs) == 0 {
+		return nil, &NotFoundError{group.Label}
+	}
+	return grs[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (gq *GroupQuery) FirstX(ctx context.Context) *Group {
+	gr, err := gq.First(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return gr
+}
+
+// FirstID returns the first Group id in the query. Returns *NotFoundError when no id was found.
+func (gq *GroupQuery) FirstID(ctx context.Context) (id int, err error) {
+	var ids []int
+	if ids, err = gq.Limit(1).IDs(ctx); err != nil {
+		return
+	}
+	if len(ids) == 0 {
+		err = &NotFoundError{group.Label}
+		return
+	}
+	return ids[0], nil
+}
+
+// FirstXID is like FirstID, but panics if an error occurs.
+func (gq *GroupQuery) FirstXID(ctx context.Context) int {
+	id, err := gq.FirstID(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return id
+}
+
+// Only returns the only Group entity in the query, returns an error if not exactly one entity was returned.
+func (gq *GroupQuery) Only(ctx context.Context) (*Group, error) {
+	grs, err := gq.Limit(2).All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	switch len(grs) {
+	case 1:
+		return grs[0], nil
+	case 0:
+		return nil, &NotFoundError{group.Label}
+	default:
+		return nil, &NotSingularError{group.Label}
+	}
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (gq *GroupQuery) OnlyX(ctx context.Context) *Group {
+	gr, err := gq.Only(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return gr
+}
+
+// OnlyID returns the only Group id in the query, returns an error if not exactly one id was returned.
+func (gq *GroupQuery) OnlyID(ctx context.Context) (id int, err error) {
+	var ids []int
+	if ids, err = gq.Limit(2).IDs(ctx); err != nil {
+		return
+	}
+	switch len(ids) {
+	case 1:
+		id = ids[0]
+	case 0:
+		err = &NotFoundError{group.Label}
+	default:
+		err = &NotSingularError{group.Label}
+	}
+	return
+}
+
+// OnlyXID is like OnlyID, but panics if an error occurs.
+func (gq *GroupQuery) OnlyXID(ctx context.Context) int {
+	id, err := gq.OnlyID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// All executes the query and returns a list of Groups.
+func (gq *GroupQuery) All(ctx context.Context) ([]*Group, error) {
+	return gq.sqlAll(ctx)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (gq *GroupQuery) AllX(ctx context.Context) []*Group {
+	grs, err := gq.All(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return grs
+}
+
+// IDs executes the query and returns a list of Group ids.
+func (gq *GroupQuery) IDs(ctx context.Context) ([]int, error) {
+	var ids []int
+	if err := gq.Select(group.FieldID).Scan(ctx, &ids); err != nil {
+		return nil, err
+	}
+	return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (gq *GroupQuery) IDsX(ctx context.Context) []int {
+	ids, err := gq.IDs(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return ids
+}
+
+// Count returns the count of the given query.
+func (gq *GroupQuery) Count(ctx context.Context) (int, error) {
+	return gq.sqlCount(ctx)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (gq *GroupQuery) CountX(ctx context.Context) int {
+	count, err := gq.Count(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (gq *GroupQuery) Exist(ctx context.Context) (bool, error) {
+	return gq.sqlExist(ctx)
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (gq *GroupQuery) ExistX(ctx context.Context) bool {
+	exist, err := gq.Exist(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return exist
+}
+
+// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (gq *GroupQuery) Clone() *GroupQuery {
+	return &GroupQuery{
+		config:     gq.config,
+		limit:      gq.limit,
+		offset:     gq.offset,
+		order:      append([]Order{}, gq.order...),
+		unique:     append([]string{}, gq.unique...),
+		predicates: append([]predicate.Group{}, gq.predicates...),
+		// clone intermediate query.
+		sql: gq.sql.Clone(),
+	}
+}
+
+//  WithUsers tells the query-builder to eager-loads the nodes that are connected to
+// the "users" edge. The optional arguments used to configure the query builder of the edge.
+func (gq *GroupQuery) WithUsers(opts ...func(*UserQuery)) *GroupQuery {
+	query := &UserQuery{config: gq.config}
+	for _, opt := range opts {
+		opt(query)
+	}
+	gq.withUsers = query
+	return gq
+}
+
+// GroupBy used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+//	var v []struct {
+//		Name string `json:"name,omitempty"`
+//		Count int `json:"count,omitempty"`
+//	}
+//
+//	client.Group.Query().
+//		GroupBy(group.FieldName).
+//		Aggregate(ent.Count()).
+//		Scan(ctx, &v)
+//
+func (gq *GroupQuery) GroupBy(field string, fields ...string) *GroupGroupBy {
+	group := &GroupGroupBy{config: gq.config}
+	group.fields = append([]string{field}, fields...)
+	group.sql = gq.sqlQuery()
+	return group
+}
+
+// Select one or more fields from the given query.
+//
+// Example:
+//
+//	var v []struct {
+//		Name string `json:"name,omitempty"`
+//	}
+//
+//	client.Group.Query().
+//		Select(group.FieldName).
+//		Scan(ctx, &v)
+//
+func (gq *GroupQuery) Select(field string, fields ...string) *GroupSelect {
+	selector := &GroupSelect{config: gq.config}
+	selector.fields = append([]string{field}, fields...)
+	selector.sql = gq.sqlQuery()
+	return selector
+}
+
+func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) {
+	var (
+		nodes       = []*Group{}
+		_spec       = gq.querySpec()
+		loadedTypes = [1]bool{
+			gq.withUsers != nil,
+		}
+	)
+	_spec.ScanValues = func() []interface{} {
+		node := &Group{config: gq.config}
+		nodes = append(nodes, node)
+		values := node.scanValues()
+		return values
+	}
+	_spec.Assign = func(values ...interface{}) error {
+		if len(nodes) == 0 {
+			return fmt.Errorf("ent: Assign called without calling ScanValues")
+		}
+		node := nodes[len(nodes)-1]
+		node.Edges.loadedTypes = loadedTypes
+		return node.assignValues(values...)
+	}
+	if err := sqlgraph.QueryNodes(ctx, gq.driver, _spec); err != nil {
+		return nil, err
+	}
+	if len(nodes) == 0 {
+		return nodes, nil
+	}
+
+	if query := gq.withUsers; query != nil {
+		fks := make([]driver.Value, 0, len(nodes))
+		ids := make(map[int]*Group, len(nodes))
+		for _, node := range nodes {
+			ids[node.ID] = node
+			fks = append(fks, node.ID)
+		}
+		var (
+			edgeids []int
+			edges   = make(map[int][]*Group)
+		)
+		_spec := &sqlgraph.EdgeQuerySpec{
+			Edge: &sqlgraph.EdgeSpec{
+				Inverse: false,
+				Table:   group.UsersTable,
+				Columns: group.UsersPrimaryKey,
+			},
+			Predicate: func(s *sql.Selector) {
+				s.Where(sql.InValues(group.UsersPrimaryKey[0], fks...))
+			},
+
+			ScanValues: func() [2]interface{} {
+				return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}}
+			},
+			Assign: func(out, in interface{}) error {
+				eout, ok := out.(*sql.NullInt64)
+				if !ok || eout == nil {
+					return fmt.Errorf("unexpected id value for edge-out")
+				}
+				ein, ok := in.(*sql.NullInt64)
+				if !ok || ein == nil {
+					return fmt.Errorf("unexpected id value for edge-in")
+				}
+				outValue := int(eout.Int64)
+				inValue := int(ein.Int64)
+				node, ok := ids[outValue]
+				if !ok {
+					return fmt.Errorf("unexpected node id in edges: %v", outValue)
+				}
+				edgeids = append(edgeids, inValue)
+				edges[inValue] = append(edges[inValue], node)
+				return nil
+			},
+		}
+		if err := sqlgraph.QueryEdges(ctx, gq.driver, _spec); err != nil {
+			return nil, fmt.Errorf(`query edges "users": %v`, err)
+		}
+		query.Where(user.IDIn(edgeids...))
+		neighbors, err := query.All(ctx)
+		if err != nil {
+			return nil, err
+		}
+		for _, n := range neighbors {
+			nodes, ok := edges[n.ID]
+			if !ok {
+				return nil, fmt.Errorf(`unexpected "users" node returned %v`, n.ID)
+			}
+			for i := range nodes {
+				nodes[i].Edges.Users = append(nodes[i].Edges.Users, n)
+			}
+		}
+	}
+
+	return nodes, nil
+}
+
+func (gq *GroupQuery) sqlCount(ctx context.Context) (int, error) {
+	_spec := gq.querySpec()
+	return sqlgraph.CountNodes(ctx, gq.driver, _spec)
+}
+
+func (gq *GroupQuery) sqlExist(ctx context.Context) (bool, error) {
+	n, err := gq.sqlCount(ctx)
+	if err != nil {
+		return false, fmt.Errorf("ent: check existence: %v", err)
+	}
+	return n > 0, nil
+}
+
+func (gq *GroupQuery) querySpec() *sqlgraph.QuerySpec {
+	_spec := &sqlgraph.QuerySpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   group.Table,
+			Columns: group.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: group.FieldID,
+			},
+		},
+		From:   gq.sql,
+		Unique: true,
+	}
+	if ps := gq.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if limit := gq.limit; limit != nil {
+		_spec.Limit = *limit
+	}
+	if offset := gq.offset; offset != nil {
+		_spec.Offset = *offset
+	}
+	if ps := gq.order; len(ps) > 0 {
+		_spec.Order = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return _spec
+}
+
+func (gq *GroupQuery) sqlQuery() *sql.Selector {
+	builder := sql.Dialect(gq.driver.Dialect())
+	t1 := builder.Table(group.Table)
+	selector := builder.Select(t1.Columns(group.Columns...)...).From(t1)
+	if gq.sql != nil {
+		selector = gq.sql
+		selector.Select(selector.Columns(group.Columns...)...)
+	}
+	for _, p := range gq.predicates {
+		p(selector)
+	}
+	for _, p := range gq.order {
+		p(selector)
+	}
+	if offset := gq.offset; offset != nil {
+		// limit is mandatory for offset clause. We start
+		// with default value, and override it below if needed.
+		selector.Offset(*offset).Limit(math.MaxInt32)
+	}
+	if limit := gq.limit; limit != nil {
+		selector.Limit(*limit)
+	}
+	return selector
+}
+
+// GroupGroupBy is the builder for group-by Group entities.
+type GroupGroupBy struct {
+	config
+	fields []string
+	fns    []Aggregate
+	// intermediate query.
+	sql *sql.Selector
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (ggb *GroupGroupBy) Aggregate(fns ...Aggregate) *GroupGroupBy {
+	ggb.fns = append(ggb.fns, fns...)
+	return ggb
+}
+
+// Scan applies the group-by query and scan the result into the given value.
+func (ggb *GroupGroupBy) Scan(ctx context.Context, v interface{}) error {
+	return ggb.sqlScan(ctx, v)
+}
+
+// ScanX is like Scan, but panics if an error occurs.
+func (ggb *GroupGroupBy) ScanX(ctx context.Context, v interface{}) {
+	if err := ggb.Scan(ctx, v); err != nil {
+		panic(err)
+	}
+}
+
+// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+func (ggb *GroupGroupBy) Strings(ctx context.Context) ([]string, error) {
+	if len(ggb.fields) > 1 {
+		return nil, errors.New("ent: GroupGroupBy.Strings is not achievable when grouping more than 1 field")
+	}
+	var v []string
+	if err := ggb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// StringsX is like Strings, but panics if an error occurs.
+func (ggb *GroupGroupBy) StringsX(ctx context.Context) []string {
+	v, err := ggb.Strings(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+func (ggb *GroupGroupBy) Ints(ctx context.Context) ([]int, error) {
+	if len(ggb.fields) > 1 {
+		return nil, errors.New("ent: GroupGroupBy.Ints is not achievable when grouping more than 1 field")
+	}
+	var v []int
+	if err := ggb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// IntsX is like Ints, but panics if an error occurs.
+func (ggb *GroupGroupBy) IntsX(ctx context.Context) []int {
+	v, err := ggb.Ints(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+func (ggb *GroupGroupBy) Float64s(ctx context.Context) ([]float64, error) {
+	if len(ggb.fields) > 1 {
+		return nil, errors.New("ent: GroupGroupBy.Float64s is not achievable when grouping more than 1 field")
+	}
+	var v []float64
+	if err := ggb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// Float64sX is like Float64s, but panics if an error occurs.
+func (ggb *GroupGroupBy) Float64sX(ctx context.Context) []float64 {
+	v, err := ggb.Float64s(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+func (ggb *GroupGroupBy) Bools(ctx context.Context) ([]bool, error) {
+	if len(ggb.fields) > 1 {
+		return nil, errors.New("ent: GroupGroupBy.Bools is not achievable when grouping more than 1 field")
+	}
+	var v []bool
+	if err := ggb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// BoolsX is like Bools, but panics if an error occurs.
+func (ggb *GroupGroupBy) BoolsX(ctx context.Context) []bool {
+	v, err := ggb.Bools(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error {
+	rows := &sql.Rows{}
+	query, args := ggb.sqlQuery().Query()
+	if err := ggb.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+func (ggb *GroupGroupBy) sqlQuery() *sql.Selector {
+	selector := ggb.sql
+	columns := make([]string, 0, len(ggb.fields)+len(ggb.fns))
+	columns = append(columns, ggb.fields...)
+	for _, fn := range ggb.fns {
+		columns = append(columns, fn(selector))
+	}
+	return selector.Select(columns...).GroupBy(ggb.fields...)
+}
+
+// GroupSelect is the builder for select fields of Group entities.
+type GroupSelect struct {
+	config
+	fields []string
+	// intermediate queries.
+	sql *sql.Selector
+}
+
+// Scan applies the selector query and scan the result into the given value.
+func (gs *GroupSelect) Scan(ctx context.Context, v interface{}) error {
+	return gs.sqlScan(ctx, v)
+}
+
+// ScanX is like Scan, but panics if an error occurs.
+func (gs *GroupSelect) ScanX(ctx context.Context, v interface{}) {
+	if err := gs.Scan(ctx, v); err != nil {
+		panic(err)
+	}
+}
+
+// Strings returns list of strings from selector. It is only allowed when selecting one field.
+func (gs *GroupSelect) Strings(ctx context.Context) ([]string, error) {
+	if len(gs.fields) > 1 {
+		return nil, errors.New("ent: GroupSelect.Strings is not achievable when selecting more than 1 field")
+	}
+	var v []string
+	if err := gs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// StringsX is like Strings, but panics if an error occurs.
+func (gs *GroupSelect) StringsX(ctx context.Context) []string {
+	v, err := gs.Strings(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Ints returns list of ints from selector. It is only allowed when selecting one field.
+func (gs *GroupSelect) Ints(ctx context.Context) ([]int, error) {
+	if len(gs.fields) > 1 {
+		return nil, errors.New("ent: GroupSelect.Ints is not achievable when selecting more than 1 field")
+	}
+	var v []int
+	if err := gs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// IntsX is like Ints, but panics if an error occurs.
+func (gs *GroupSelect) IntsX(ctx context.Context) []int {
+	v, err := gs.Ints(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+func (gs *GroupSelect) Float64s(ctx context.Context) ([]float64, error) {
+	if len(gs.fields) > 1 {
+		return nil, errors.New("ent: GroupSelect.Float64s is not achievable when selecting more than 1 field")
+	}
+	var v []float64
+	if err := gs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// Float64sX is like Float64s, but panics if an error occurs.
+func (gs *GroupSelect) Float64sX(ctx context.Context) []float64 {
+	v, err := gs.Float64s(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Bools returns list of bools from selector. It is only allowed when selecting one field.
+func (gs *GroupSelect) Bools(ctx context.Context) ([]bool, error) {
+	if len(gs.fields) > 1 {
+		return nil, errors.New("ent: GroupSelect.Bools is not achievable when selecting more than 1 field")
+	}
+	var v []bool
+	if err := gs.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// BoolsX is like Bools, but panics if an error occurs.
+func (gs *GroupSelect) BoolsX(ctx context.Context) []bool {
+	v, err := gs.Bools(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (gs *GroupSelect) sqlScan(ctx context.Context, v interface{}) error {
+	rows := &sql.Rows{}
+	query, args := gs.sqlQuery().Query()
+	if err := gs.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+func (gs *GroupSelect) sqlQuery() sql.Querier {
+	selector := gs.sql
+	selector.Select(selector.Columns(gs.fields...)...)
+	return selector
+}

+ 369 - 0
ent/group_update.go

@@ -0,0 +1,369 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// GroupUpdate is the builder for updating Group entities.
+type GroupUpdate struct {
+	config
+	hooks      []Hook
+	mutation   *GroupMutation
+	predicates []predicate.Group
+}
+
+// Where adds a new predicate for the builder.
+func (gu *GroupUpdate) Where(ps ...predicate.Group) *GroupUpdate {
+	gu.predicates = append(gu.predicates, ps...)
+	return gu
+}
+
+// SetName sets the name field.
+func (gu *GroupUpdate) SetName(s string) *GroupUpdate {
+	gu.mutation.SetName(s)
+	return gu
+}
+
+// AddUserIDs adds the users edge to User by ids.
+func (gu *GroupUpdate) AddUserIDs(ids ...int) *GroupUpdate {
+	gu.mutation.AddUserIDs(ids...)
+	return gu
+}
+
+// AddUsers adds the users edges to User.
+func (gu *GroupUpdate) AddUsers(u ...*User) *GroupUpdate {
+	ids := make([]int, len(u))
+	for i := range u {
+		ids[i] = u[i].ID
+	}
+	return gu.AddUserIDs(ids...)
+}
+
+// RemoveUserIDs removes the users edge to User by ids.
+func (gu *GroupUpdate) RemoveUserIDs(ids ...int) *GroupUpdate {
+	gu.mutation.RemoveUserIDs(ids...)
+	return gu
+}
+
+// RemoveUsers removes users edges to User.
+func (gu *GroupUpdate) RemoveUsers(u ...*User) *GroupUpdate {
+	ids := make([]int, len(u))
+	for i := range u {
+		ids[i] = u[i].ID
+	}
+	return gu.RemoveUserIDs(ids...)
+}
+
+// Save executes the query and returns the number of rows/vertices matched by this operation.
+func (gu *GroupUpdate) Save(ctx context.Context) (int, error) {
+	if v, ok := gu.mutation.Name(); ok {
+		if err := group.NameValidator(v); err != nil {
+			return 0, fmt.Errorf("ent: validator failed for field \"name\": %v", err)
+		}
+	}
+
+	var (
+		err      error
+		affected int
+	)
+	if len(gu.hooks) == 0 {
+		affected, err = gu.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*GroupMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			gu.mutation = mutation
+			affected, err = gu.sqlSave(ctx)
+			return affected, err
+		})
+		for i := len(gu.hooks) - 1; i >= 0; i-- {
+			mut = gu.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, gu.mutation); err != nil {
+			return 0, err
+		}
+	}
+	return affected, err
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (gu *GroupUpdate) SaveX(ctx context.Context) int {
+	affected, err := gu.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (gu *GroupUpdate) Exec(ctx context.Context) error {
+	_, err := gu.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (gu *GroupUpdate) ExecX(ctx context.Context) {
+	if err := gu.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	_spec := &sqlgraph.UpdateSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   group.Table,
+			Columns: group.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: group.FieldID,
+			},
+		},
+	}
+	if ps := gu.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := gu.mutation.Name(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: group.FieldName,
+		})
+	}
+	if nodes := gu.mutation.RemovedUsersIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: false,
+			Table:   group.UsersTable,
+			Columns: group.UsersPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := gu.mutation.UsersIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: false,
+			Table:   group.UsersTable,
+			Columns: group.UsersPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if n, err = sqlgraph.UpdateNodes(ctx, gu.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{group.Label}
+		} else if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return 0, err
+	}
+	return n, nil
+}
+
+// GroupUpdateOne is the builder for updating a single Group entity.
+type GroupUpdateOne struct {
+	config
+	hooks    []Hook
+	mutation *GroupMutation
+}
+
+// SetName sets the name field.
+func (guo *GroupUpdateOne) SetName(s string) *GroupUpdateOne {
+	guo.mutation.SetName(s)
+	return guo
+}
+
+// AddUserIDs adds the users edge to User by ids.
+func (guo *GroupUpdateOne) AddUserIDs(ids ...int) *GroupUpdateOne {
+	guo.mutation.AddUserIDs(ids...)
+	return guo
+}
+
+// AddUsers adds the users edges to User.
+func (guo *GroupUpdateOne) AddUsers(u ...*User) *GroupUpdateOne {
+	ids := make([]int, len(u))
+	for i := range u {
+		ids[i] = u[i].ID
+	}
+	return guo.AddUserIDs(ids...)
+}
+
+// RemoveUserIDs removes the users edge to User by ids.
+func (guo *GroupUpdateOne) RemoveUserIDs(ids ...int) *GroupUpdateOne {
+	guo.mutation.RemoveUserIDs(ids...)
+	return guo
+}
+
+// RemoveUsers removes users edges to User.
+func (guo *GroupUpdateOne) RemoveUsers(u ...*User) *GroupUpdateOne {
+	ids := make([]int, len(u))
+	for i := range u {
+		ids[i] = u[i].ID
+	}
+	return guo.RemoveUserIDs(ids...)
+}
+
+// Save executes the query and returns the updated entity.
+func (guo *GroupUpdateOne) Save(ctx context.Context) (*Group, error) {
+	if v, ok := guo.mutation.Name(); ok {
+		if err := group.NameValidator(v); err != nil {
+			return nil, fmt.Errorf("ent: validator failed for field \"name\": %v", err)
+		}
+	}
+
+	var (
+		err  error
+		node *Group
+	)
+	if len(guo.hooks) == 0 {
+		node, err = guo.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*GroupMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			guo.mutation = mutation
+			node, err = guo.sqlSave(ctx)
+			return node, err
+		})
+		for i := len(guo.hooks) - 1; i >= 0; i-- {
+			mut = guo.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, guo.mutation); err != nil {
+			return nil, err
+		}
+	}
+	return node, err
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (guo *GroupUpdateOne) SaveX(ctx context.Context) *Group {
+	gr, err := guo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return gr
+}
+
+// Exec executes the query on the entity.
+func (guo *GroupUpdateOne) Exec(ctx context.Context) error {
+	_, err := guo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (guo *GroupUpdateOne) ExecX(ctx context.Context) {
+	if err := guo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (gr *Group, err error) {
+	_spec := &sqlgraph.UpdateSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   group.Table,
+			Columns: group.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: group.FieldID,
+			},
+		},
+	}
+	id, ok := guo.mutation.ID()
+	if !ok {
+		return nil, fmt.Errorf("missing Group.ID for update")
+	}
+	_spec.Node.ID.Value = id
+	if value, ok := guo.mutation.Name(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: group.FieldName,
+		})
+	}
+	if nodes := guo.mutation.RemovedUsersIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: false,
+			Table:   group.UsersTable,
+			Columns: group.UsersPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := guo.mutation.UsersIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: false,
+			Table:   group.UsersTable,
+			Columns: group.UsersPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: user.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	gr = &Group{config: guo.config}
+	_spec.Assign = gr.assignValues
+	_spec.ScanValues = gr.scanValues()
+	if err = sqlgraph.UpdateNode(ctx, guo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{group.Label}
+		} else if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return nil, err
+	}
+	return gr, nil
+}

+ 83 - 0
ent/hook/hook.go

@@ -0,0 +1,83 @@
+// Code generated by entc, DO NOT EDIT.
+
+package hook
+
+import (
+	"context"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent"
+)
+
+// The CarFunc type is an adapter to allow the use of ordinary
+// function as Car mutator.
+type CarFunc func(context.Context, *ent.CarMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f CarFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	mv, ok := m.(*ent.CarMutation)
+	if !ok {
+		return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CarMutation", m)
+	}
+	return f(ctx, mv)
+}
+
+// The GroupFunc type is an adapter to allow the use of ordinary
+// function as Group mutator.
+type GroupFunc func(context.Context, *ent.GroupMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f GroupFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	mv, ok := m.(*ent.GroupMutation)
+	if !ok {
+		return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.GroupMutation", m)
+	}
+	return f(ctx, mv)
+}
+
+// The UserFunc type is an adapter to allow the use of ordinary
+// function as User mutator.
+type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	mv, ok := m.(*ent.UserMutation)
+	if !ok {
+		return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m)
+	}
+	return f(ctx, mv)
+}
+
+// On executes the given hook only of the given operation.
+//
+//	hook.On(Log, ent.Delete|ent.Create)
+//
+func On(hk ent.Hook, op ent.Op) ent.Hook {
+	return func(next ent.Mutator) ent.Mutator {
+		return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+			if m.Op().Is(op) {
+				return hk(next).Mutate(ctx, m)
+			}
+			return next.Mutate(ctx, m)
+		})
+	}
+}
+
+// Reject returns a hook that rejects all operations that match op.
+//
+//	func (T) Hooks() []ent.Hook {
+//		return []ent.Hook{
+//			Reject(ent.Delete|ent.Update),
+//		}
+//	}
+//
+func Reject(op ent.Op) ent.Hook {
+	return func(next ent.Mutator) ent.Mutator {
+		return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+			if m.Op().Is(op) {
+				return nil, fmt.Errorf("%s operation is not allowed", m.Op())
+			}
+			return next.Mutate(ctx, m)
+		})
+	}
+}

+ 70 - 0
ent/migrate/migrate.go

@@ -0,0 +1,70 @@
+// Code generated by entc, DO NOT EDIT.
+
+package migrate
+
+import (
+	"context"
+	"fmt"
+	"io"
+
+	"github.com/facebookincubator/ent/dialect"
+	"github.com/facebookincubator/ent/dialect/sql/schema"
+)
+
+var (
+	// WithGlobalUniqueID sets the universal ids options to the migration.
+	// If this option is enabled, ent migration will allocate a 1<<32 range
+	// for the ids of each entity (table).
+	// Note that this option cannot be applied on tables that already exist.
+	WithGlobalUniqueID = schema.WithGlobalUniqueID
+	// WithDropColumn sets the drop column option to the migration.
+	// If this option is enabled, ent migration will drop old columns
+	// that were used for both fields and edges. This defaults to false.
+	WithDropColumn = schema.WithDropColumn
+	// WithDropIndex sets the drop index option to the migration.
+	// If this option is enabled, ent migration will drop old indexes
+	// that were defined in the schema. This defaults to false.
+	// Note that unique constraints are defined using `UNIQUE INDEX`,
+	// and therefore, it's recommended to enable this option to get more
+	// flexibility in the schema changes.
+	WithDropIndex = schema.WithDropIndex
+	// WithFixture sets the foreign-key renaming option to the migration when upgrading
+	// ent from v0.1.0 (issue-#285). Defaults to true.
+	WithFixture = schema.WithFixture
+)
+
+// Schema is the API for creating, migrating and dropping a schema.
+type Schema struct {
+	drv         dialect.Driver
+	universalID bool
+}
+
+// NewSchema creates a new schema client.
+func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} }
+
+// Create creates all schema resources.
+func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error {
+	migrate, err := schema.NewMigrate(s.drv, opts...)
+	if err != nil {
+		return fmt.Errorf("ent/migrate: %v", err)
+	}
+	return migrate.Create(ctx, Tables...)
+}
+
+// WriteTo writes the schema changes to w instead of running them against the database.
+//
+// 	if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil {
+//		log.Fatal(err)
+// 	}
+//
+func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error {
+	drv := &schema.WriteDriver{
+		Writer: w,
+		Driver: s.drv,
+	}
+	migrate, err := schema.NewMigrate(drv, opts...)
+	if err != nil {
+		return fmt.Errorf("ent/migrate: %v", err)
+	}
+	return migrate.Create(ctx, Tables...)
+}

+ 98 - 0
ent/migrate/schema.go

@@ -0,0 +1,98 @@
+// Code generated by entc, DO NOT EDIT.
+
+package migrate
+
+import (
+	"github.com/facebookincubator/ent/dialect/sql/schema"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+var (
+	// CarsColumns holds the columns for the "cars" table.
+	CarsColumns = []*schema.Column{
+		{Name: "id", Type: field.TypeInt, Increment: true},
+		{Name: "model", Type: field.TypeString},
+		{Name: "registered_at", Type: field.TypeTime},
+		{Name: "user_cars", Type: field.TypeInt, Nullable: true},
+	}
+	// CarsTable holds the schema information for the "cars" table.
+	CarsTable = &schema.Table{
+		Name:       "cars",
+		Columns:    CarsColumns,
+		PrimaryKey: []*schema.Column{CarsColumns[0]},
+		ForeignKeys: []*schema.ForeignKey{
+			{
+				Symbol:  "cars_users_cars",
+				Columns: []*schema.Column{CarsColumns[3]},
+
+				RefColumns: []*schema.Column{UsersColumns[0]},
+				OnDelete:   schema.SetNull,
+			},
+		},
+	}
+	// GroupsColumns holds the columns for the "groups" table.
+	GroupsColumns = []*schema.Column{
+		{Name: "id", Type: field.TypeInt, Increment: true},
+		{Name: "name", Type: field.TypeString},
+	}
+	// GroupsTable holds the schema information for the "groups" table.
+	GroupsTable = &schema.Table{
+		Name:        "groups",
+		Columns:     GroupsColumns,
+		PrimaryKey:  []*schema.Column{GroupsColumns[0]},
+		ForeignKeys: []*schema.ForeignKey{},
+	}
+	// UsersColumns holds the columns for the "users" table.
+	UsersColumns = []*schema.Column{
+		{Name: "id", Type: field.TypeInt, Increment: true},
+		{Name: "age", Type: field.TypeInt},
+		{Name: "name", Type: field.TypeString, Default: "unknown"},
+	}
+	// UsersTable holds the schema information for the "users" table.
+	UsersTable = &schema.Table{
+		Name:        "users",
+		Columns:     UsersColumns,
+		PrimaryKey:  []*schema.Column{UsersColumns[0]},
+		ForeignKeys: []*schema.ForeignKey{},
+	}
+	// GroupUsersColumns holds the columns for the "group_users" table.
+	GroupUsersColumns = []*schema.Column{
+		{Name: "group_id", Type: field.TypeInt},
+		{Name: "user_id", Type: field.TypeInt},
+	}
+	// GroupUsersTable holds the schema information for the "group_users" table.
+	GroupUsersTable = &schema.Table{
+		Name:       "group_users",
+		Columns:    GroupUsersColumns,
+		PrimaryKey: []*schema.Column{GroupUsersColumns[0], GroupUsersColumns[1]},
+		ForeignKeys: []*schema.ForeignKey{
+			{
+				Symbol:  "group_users_group_id",
+				Columns: []*schema.Column{GroupUsersColumns[0]},
+
+				RefColumns: []*schema.Column{GroupsColumns[0]},
+				OnDelete:   schema.Cascade,
+			},
+			{
+				Symbol:  "group_users_user_id",
+				Columns: []*schema.Column{GroupUsersColumns[1]},
+
+				RefColumns: []*schema.Column{UsersColumns[0]},
+				OnDelete:   schema.Cascade,
+			},
+		},
+	}
+	// Tables holds all the tables in the schema.
+	Tables = []*schema.Table{
+		CarsTable,
+		GroupsTable,
+		UsersTable,
+		GroupUsersTable,
+	}
+)
+
+func init() {
+	CarsTable.ForeignKeys[0].RefTable = UsersTable
+	GroupUsersTable.ForeignKeys[0].RefTable = GroupsTable
+	GroupUsersTable.ForeignKeys[1].RefTable = UsersTable
+}

+ 1087 - 0
ent/mutation.go

@@ -0,0 +1,1087 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"fmt"
+	"time"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+
+	"github.com/facebookincubator/ent"
+)
+
+const (
+	// Operation types.
+	OpCreate    = ent.OpCreate
+	OpDelete    = ent.OpDelete
+	OpDeleteOne = ent.OpDeleteOne
+	OpUpdate    = ent.OpUpdate
+	OpUpdateOne = ent.OpUpdateOne
+
+	// Node types.
+	TypeCar   = "Car"
+	TypeGroup = "Group"
+	TypeUser  = "User"
+)
+
+// CarMutation represents an operation that mutate the Cars
+// nodes in the graph.
+type CarMutation struct {
+	config
+	op            Op
+	typ           string
+	id            *int
+	model         *string
+	registered_at *time.Time
+	clearedFields map[string]struct{}
+	owner         *int
+	clearedowner  bool
+}
+
+var _ ent.Mutation = (*CarMutation)(nil)
+
+// newCarMutation creates new mutation for $n.Name.
+func newCarMutation(c config, op Op) *CarMutation {
+	return &CarMutation{
+		config:        c,
+		op:            op,
+		typ:           TypeCar,
+		clearedFields: make(map[string]struct{}),
+	}
+}
+
+// Client returns a new `ent.Client` from the mutation. If the mutation was
+// executed in a transaction (ent.Tx), a transactional client is returned.
+func (m CarMutation) Client() *Client {
+	client := &Client{config: m.config}
+	client.init()
+	return client
+}
+
+// Tx returns an `ent.Tx` for mutations that were executed in transactions;
+// it returns an error otherwise.
+func (m CarMutation) Tx() (*Tx, error) {
+	if _, ok := m.driver.(*txDriver); !ok {
+		return nil, fmt.Errorf("ent: mutation is not running in a transaction")
+	}
+	tx := &Tx{config: m.config}
+	tx.init()
+	return tx, nil
+}
+
+// ID returns the id value in the mutation. Note that, the id
+// is available only if it was provided to the builder.
+func (m *CarMutation) ID() (id int, exists bool) {
+	if m.id == nil {
+		return
+	}
+	return *m.id, true
+}
+
+// SetModel sets the model field.
+func (m *CarMutation) SetModel(s string) {
+	m.model = &s
+}
+
+// Model returns the model value in the mutation.
+func (m *CarMutation) Model() (r string, exists bool) {
+	v := m.model
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// ResetModel reset all changes of the model field.
+func (m *CarMutation) ResetModel() {
+	m.model = nil
+}
+
+// SetRegisteredAt sets the registered_at field.
+func (m *CarMutation) SetRegisteredAt(t time.Time) {
+	m.registered_at = &t
+}
+
+// RegisteredAt returns the registered_at value in the mutation.
+func (m *CarMutation) RegisteredAt() (r time.Time, exists bool) {
+	v := m.registered_at
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// ResetRegisteredAt reset all changes of the registered_at field.
+func (m *CarMutation) ResetRegisteredAt() {
+	m.registered_at = nil
+}
+
+// SetOwnerID sets the owner edge to User by id.
+func (m *CarMutation) SetOwnerID(id int) {
+	m.owner = &id
+}
+
+// ClearOwner clears the owner edge to User.
+func (m *CarMutation) ClearOwner() {
+	m.clearedowner = true
+}
+
+// OwnerCleared returns if the edge owner was cleared.
+func (m *CarMutation) OwnerCleared() bool {
+	return m.clearedowner
+}
+
+// OwnerID returns the owner id in the mutation.
+func (m *CarMutation) OwnerID() (id int, exists bool) {
+	if m.owner != nil {
+		return *m.owner, true
+	}
+	return
+}
+
+// OwnerIDs returns the owner ids in the mutation.
+// Note that ids always returns len(ids) <= 1 for unique edges, and you should use
+// OwnerID instead. It exists only for internal usage by the builders.
+func (m *CarMutation) OwnerIDs() (ids []int) {
+	if id := m.owner; id != nil {
+		ids = append(ids, *id)
+	}
+	return
+}
+
+// ResetOwner reset all changes of the owner edge.
+func (m *CarMutation) ResetOwner() {
+	m.owner = nil
+	m.clearedowner = false
+}
+
+// Op returns the operation name.
+func (m *CarMutation) Op() Op {
+	return m.op
+}
+
+// Type returns the node type of this mutation (Car).
+func (m *CarMutation) Type() string {
+	return m.typ
+}
+
+// Fields returns all fields that were changed during
+// this mutation. Note that, in order to get all numeric
+// fields that were in/decremented, call AddedFields().
+func (m *CarMutation) Fields() []string {
+	fields := make([]string, 0, 2)
+	if m.model != nil {
+		fields = append(fields, car.FieldModel)
+	}
+	if m.registered_at != nil {
+		fields = append(fields, car.FieldRegisteredAt)
+	}
+	return fields
+}
+
+// Field returns the value of a field with the given name.
+// The second boolean value indicates that this field was
+// not set, or was not define in the schema.
+func (m *CarMutation) Field(name string) (ent.Value, bool) {
+	switch name {
+	case car.FieldModel:
+		return m.Model()
+	case car.FieldRegisteredAt:
+		return m.RegisteredAt()
+	}
+	return nil, false
+}
+
+// SetField sets the value for the given name. It returns an
+// error if the field is not defined in the schema, or if the
+// type mismatch the field type.
+func (m *CarMutation) SetField(name string, value ent.Value) error {
+	switch name {
+	case car.FieldModel:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetModel(v)
+		return nil
+	case car.FieldRegisteredAt:
+		v, ok := value.(time.Time)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetRegisteredAt(v)
+		return nil
+	}
+	return fmt.Errorf("unknown Car field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented
+// or decremented during this mutation.
+func (m *CarMutation) AddedFields() []string {
+	return nil
+}
+
+// AddedField returns the numeric value that was in/decremented
+// from a field with the given name. The second value indicates
+// that this field was not set, or was not define in the schema.
+func (m *CarMutation) AddedField(name string) (ent.Value, bool) {
+	return nil, false
+}
+
+// AddField adds the value for the given name. It returns an
+// error if the field is not defined in the schema, or if the
+// type mismatch the field type.
+func (m *CarMutation) AddField(name string, value ent.Value) error {
+	switch name {
+	}
+	return fmt.Errorf("unknown Car numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared
+// during this mutation.
+func (m *CarMutation) ClearedFields() []string {
+	return nil
+}
+
+// FieldCleared returns a boolean indicates if this field was
+// cleared in this mutation.
+func (m *CarMutation) FieldCleared(name string) bool {
+	_, ok := m.clearedFields[name]
+	return ok
+}
+
+// ClearField clears the value for the given name. It returns an
+// error if the field is not defined in the schema.
+func (m *CarMutation) ClearField(name string) error {
+	return fmt.Errorf("unknown Car nullable field %s", name)
+}
+
+// ResetField resets all changes in the mutation regarding the
+// given field name. It returns an error if the field is not
+// defined in the schema.
+func (m *CarMutation) ResetField(name string) error {
+	switch name {
+	case car.FieldModel:
+		m.ResetModel()
+		return nil
+	case car.FieldRegisteredAt:
+		m.ResetRegisteredAt()
+		return nil
+	}
+	return fmt.Errorf("unknown Car field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this
+// mutation.
+func (m *CarMutation) AddedEdges() []string {
+	edges := make([]string, 0, 1)
+	if m.owner != nil {
+		edges = append(edges, car.EdgeOwner)
+	}
+	return edges
+}
+
+// AddedIDs returns all ids (to other nodes) that were added for
+// the given edge name.
+func (m *CarMutation) AddedIDs(name string) []ent.Value {
+	switch name {
+	case car.EdgeOwner:
+		if id := m.owner; id != nil {
+			return []ent.Value{*id}
+		}
+	}
+	return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this
+// mutation.
+func (m *CarMutation) RemovedEdges() []string {
+	edges := make([]string, 0, 1)
+	return edges
+}
+
+// RemovedIDs returns all ids (to other nodes) that were removed for
+// the given edge name.
+func (m *CarMutation) RemovedIDs(name string) []ent.Value {
+	switch name {
+	}
+	return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this
+// mutation.
+func (m *CarMutation) ClearedEdges() []string {
+	edges := make([]string, 0, 1)
+	if m.clearedowner {
+		edges = append(edges, car.EdgeOwner)
+	}
+	return edges
+}
+
+// EdgeCleared returns a boolean indicates if this edge was
+// cleared in this mutation.
+func (m *CarMutation) EdgeCleared(name string) bool {
+	switch name {
+	case car.EdgeOwner:
+		return m.clearedowner
+	}
+	return false
+}
+
+// ClearEdge clears the value for the given name. It returns an
+// error if the edge name is not defined in the schema.
+func (m *CarMutation) ClearEdge(name string) error {
+	switch name {
+	case car.EdgeOwner:
+		m.ClearOwner()
+		return nil
+	}
+	return fmt.Errorf("unknown Car unique edge %s", name)
+}
+
+// ResetEdge resets all changes in the mutation regarding the
+// given edge name. It returns an error if the edge is not
+// defined in the schema.
+func (m *CarMutation) ResetEdge(name string) error {
+	switch name {
+	case car.EdgeOwner:
+		m.ResetOwner()
+		return nil
+	}
+	return fmt.Errorf("unknown Car edge %s", name)
+}
+
+// GroupMutation represents an operation that mutate the Groups
+// nodes in the graph.
+type GroupMutation struct {
+	config
+	op            Op
+	typ           string
+	id            *int
+	name          *string
+	clearedFields map[string]struct{}
+	users         map[int]struct{}
+	removedusers  map[int]struct{}
+}
+
+var _ ent.Mutation = (*GroupMutation)(nil)
+
+// newGroupMutation creates new mutation for $n.Name.
+func newGroupMutation(c config, op Op) *GroupMutation {
+	return &GroupMutation{
+		config:        c,
+		op:            op,
+		typ:           TypeGroup,
+		clearedFields: make(map[string]struct{}),
+	}
+}
+
+// Client returns a new `ent.Client` from the mutation. If the mutation was
+// executed in a transaction (ent.Tx), a transactional client is returned.
+func (m GroupMutation) Client() *Client {
+	client := &Client{config: m.config}
+	client.init()
+	return client
+}
+
+// Tx returns an `ent.Tx` for mutations that were executed in transactions;
+// it returns an error otherwise.
+func (m GroupMutation) Tx() (*Tx, error) {
+	if _, ok := m.driver.(*txDriver); !ok {
+		return nil, fmt.Errorf("ent: mutation is not running in a transaction")
+	}
+	tx := &Tx{config: m.config}
+	tx.init()
+	return tx, nil
+}
+
+// ID returns the id value in the mutation. Note that, the id
+// is available only if it was provided to the builder.
+func (m *GroupMutation) ID() (id int, exists bool) {
+	if m.id == nil {
+		return
+	}
+	return *m.id, true
+}
+
+// SetName sets the name field.
+func (m *GroupMutation) SetName(s string) {
+	m.name = &s
+}
+
+// Name returns the name value in the mutation.
+func (m *GroupMutation) Name() (r string, exists bool) {
+	v := m.name
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// ResetName reset all changes of the name field.
+func (m *GroupMutation) ResetName() {
+	m.name = nil
+}
+
+// AddUserIDs adds the users edge to User by ids.
+func (m *GroupMutation) AddUserIDs(ids ...int) {
+	if m.users == nil {
+		m.users = make(map[int]struct{})
+	}
+	for i := range ids {
+		m.users[ids[i]] = struct{}{}
+	}
+}
+
+// RemoveUserIDs removes the users edge to User by ids.
+func (m *GroupMutation) RemoveUserIDs(ids ...int) {
+	if m.removedusers == nil {
+		m.removedusers = make(map[int]struct{})
+	}
+	for i := range ids {
+		m.removedusers[ids[i]] = struct{}{}
+	}
+}
+
+// RemovedUsers returns the removed ids of users.
+func (m *GroupMutation) RemovedUsersIDs() (ids []int) {
+	for id := range m.removedusers {
+		ids = append(ids, id)
+	}
+	return
+}
+
+// UsersIDs returns the users ids in the mutation.
+func (m *GroupMutation) UsersIDs() (ids []int) {
+	for id := range m.users {
+		ids = append(ids, id)
+	}
+	return
+}
+
+// ResetUsers reset all changes of the users edge.
+func (m *GroupMutation) ResetUsers() {
+	m.users = nil
+	m.removedusers = nil
+}
+
+// Op returns the operation name.
+func (m *GroupMutation) Op() Op {
+	return m.op
+}
+
+// Type returns the node type of this mutation (Group).
+func (m *GroupMutation) Type() string {
+	return m.typ
+}
+
+// Fields returns all fields that were changed during
+// this mutation. Note that, in order to get all numeric
+// fields that were in/decremented, call AddedFields().
+func (m *GroupMutation) Fields() []string {
+	fields := make([]string, 0, 1)
+	if m.name != nil {
+		fields = append(fields, group.FieldName)
+	}
+	return fields
+}
+
+// Field returns the value of a field with the given name.
+// The second boolean value indicates that this field was
+// not set, or was not define in the schema.
+func (m *GroupMutation) Field(name string) (ent.Value, bool) {
+	switch name {
+	case group.FieldName:
+		return m.Name()
+	}
+	return nil, false
+}
+
+// SetField sets the value for the given name. It returns an
+// error if the field is not defined in the schema, or if the
+// type mismatch the field type.
+func (m *GroupMutation) SetField(name string, value ent.Value) error {
+	switch name {
+	case group.FieldName:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetName(v)
+		return nil
+	}
+	return fmt.Errorf("unknown Group field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented
+// or decremented during this mutation.
+func (m *GroupMutation) AddedFields() []string {
+	return nil
+}
+
+// AddedField returns the numeric value that was in/decremented
+// from a field with the given name. The second value indicates
+// that this field was not set, or was not define in the schema.
+func (m *GroupMutation) AddedField(name string) (ent.Value, bool) {
+	return nil, false
+}
+
+// AddField adds the value for the given name. It returns an
+// error if the field is not defined in the schema, or if the
+// type mismatch the field type.
+func (m *GroupMutation) AddField(name string, value ent.Value) error {
+	switch name {
+	}
+	return fmt.Errorf("unknown Group numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared
+// during this mutation.
+func (m *GroupMutation) ClearedFields() []string {
+	return nil
+}
+
+// FieldCleared returns a boolean indicates if this field was
+// cleared in this mutation.
+func (m *GroupMutation) FieldCleared(name string) bool {
+	_, ok := m.clearedFields[name]
+	return ok
+}
+
+// ClearField clears the value for the given name. It returns an
+// error if the field is not defined in the schema.
+func (m *GroupMutation) ClearField(name string) error {
+	return fmt.Errorf("unknown Group nullable field %s", name)
+}
+
+// ResetField resets all changes in the mutation regarding the
+// given field name. It returns an error if the field is not
+// defined in the schema.
+func (m *GroupMutation) ResetField(name string) error {
+	switch name {
+	case group.FieldName:
+		m.ResetName()
+		return nil
+	}
+	return fmt.Errorf("unknown Group field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this
+// mutation.
+func (m *GroupMutation) AddedEdges() []string {
+	edges := make([]string, 0, 1)
+	if m.users != nil {
+		edges = append(edges, group.EdgeUsers)
+	}
+	return edges
+}
+
+// AddedIDs returns all ids (to other nodes) that were added for
+// the given edge name.
+func (m *GroupMutation) AddedIDs(name string) []ent.Value {
+	switch name {
+	case group.EdgeUsers:
+		ids := make([]ent.Value, 0, len(m.users))
+		for id := range m.users {
+			ids = append(ids, id)
+		}
+		return ids
+	}
+	return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this
+// mutation.
+func (m *GroupMutation) RemovedEdges() []string {
+	edges := make([]string, 0, 1)
+	if m.removedusers != nil {
+		edges = append(edges, group.EdgeUsers)
+	}
+	return edges
+}
+
+// RemovedIDs returns all ids (to other nodes) that were removed for
+// the given edge name.
+func (m *GroupMutation) RemovedIDs(name string) []ent.Value {
+	switch name {
+	case group.EdgeUsers:
+		ids := make([]ent.Value, 0, len(m.removedusers))
+		for id := range m.removedusers {
+			ids = append(ids, id)
+		}
+		return ids
+	}
+	return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this
+// mutation.
+func (m *GroupMutation) ClearedEdges() []string {
+	edges := make([]string, 0, 1)
+	return edges
+}
+
+// EdgeCleared returns a boolean indicates if this edge was
+// cleared in this mutation.
+func (m *GroupMutation) EdgeCleared(name string) bool {
+	switch name {
+	}
+	return false
+}
+
+// ClearEdge clears the value for the given name. It returns an
+// error if the edge name is not defined in the schema.
+func (m *GroupMutation) ClearEdge(name string) error {
+	switch name {
+	}
+	return fmt.Errorf("unknown Group unique edge %s", name)
+}
+
+// ResetEdge resets all changes in the mutation regarding the
+// given edge name. It returns an error if the edge is not
+// defined in the schema.
+func (m *GroupMutation) ResetEdge(name string) error {
+	switch name {
+	case group.EdgeUsers:
+		m.ResetUsers()
+		return nil
+	}
+	return fmt.Errorf("unknown Group edge %s", name)
+}
+
+// UserMutation represents an operation that mutate the Users
+// nodes in the graph.
+type UserMutation struct {
+	config
+	op            Op
+	typ           string
+	id            *int
+	age           *int
+	addage        *int
+	name          *string
+	clearedFields map[string]struct{}
+	cars          map[int]struct{}
+	removedcars   map[int]struct{}
+	groups        map[int]struct{}
+	removedgroups map[int]struct{}
+}
+
+var _ ent.Mutation = (*UserMutation)(nil)
+
+// newUserMutation creates new mutation for $n.Name.
+func newUserMutation(c config, op Op) *UserMutation {
+	return &UserMutation{
+		config:        c,
+		op:            op,
+		typ:           TypeUser,
+		clearedFields: make(map[string]struct{}),
+	}
+}
+
+// Client returns a new `ent.Client` from the mutation. If the mutation was
+// executed in a transaction (ent.Tx), a transactional client is returned.
+func (m UserMutation) Client() *Client {
+	client := &Client{config: m.config}
+	client.init()
+	return client
+}
+
+// Tx returns an `ent.Tx` for mutations that were executed in transactions;
+// it returns an error otherwise.
+func (m UserMutation) Tx() (*Tx, error) {
+	if _, ok := m.driver.(*txDriver); !ok {
+		return nil, fmt.Errorf("ent: mutation is not running in a transaction")
+	}
+	tx := &Tx{config: m.config}
+	tx.init()
+	return tx, nil
+}
+
+// ID returns the id value in the mutation. Note that, the id
+// is available only if it was provided to the builder.
+func (m *UserMutation) ID() (id int, exists bool) {
+	if m.id == nil {
+		return
+	}
+	return *m.id, true
+}
+
+// SetAge sets the age field.
+func (m *UserMutation) SetAge(i int) {
+	m.age = &i
+	m.addage = nil
+}
+
+// Age returns the age value in the mutation.
+func (m *UserMutation) Age() (r int, exists bool) {
+	v := m.age
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// AddAge adds i to age.
+func (m *UserMutation) AddAge(i int) {
+	if m.addage != nil {
+		*m.addage += i
+	} else {
+		m.addage = &i
+	}
+}
+
+// AddedAge returns the value that was added to the age field in this mutation.
+func (m *UserMutation) AddedAge() (r int, exists bool) {
+	v := m.addage
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// ResetAge reset all changes of the age field.
+func (m *UserMutation) ResetAge() {
+	m.age = nil
+	m.addage = nil
+}
+
+// SetName sets the name field.
+func (m *UserMutation) SetName(s string) {
+	m.name = &s
+}
+
+// Name returns the name value in the mutation.
+func (m *UserMutation) Name() (r string, exists bool) {
+	v := m.name
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// ResetName reset all changes of the name field.
+func (m *UserMutation) ResetName() {
+	m.name = nil
+}
+
+// AddCarIDs adds the cars edge to Car by ids.
+func (m *UserMutation) AddCarIDs(ids ...int) {
+	if m.cars == nil {
+		m.cars = make(map[int]struct{})
+	}
+	for i := range ids {
+		m.cars[ids[i]] = struct{}{}
+	}
+}
+
+// RemoveCarIDs removes the cars edge to Car by ids.
+func (m *UserMutation) RemoveCarIDs(ids ...int) {
+	if m.removedcars == nil {
+		m.removedcars = make(map[int]struct{})
+	}
+	for i := range ids {
+		m.removedcars[ids[i]] = struct{}{}
+	}
+}
+
+// RemovedCars returns the removed ids of cars.
+func (m *UserMutation) RemovedCarsIDs() (ids []int) {
+	for id := range m.removedcars {
+		ids = append(ids, id)
+	}
+	return
+}
+
+// CarsIDs returns the cars ids in the mutation.
+func (m *UserMutation) CarsIDs() (ids []int) {
+	for id := range m.cars {
+		ids = append(ids, id)
+	}
+	return
+}
+
+// ResetCars reset all changes of the cars edge.
+func (m *UserMutation) ResetCars() {
+	m.cars = nil
+	m.removedcars = nil
+}
+
+// AddGroupIDs adds the groups edge to Group by ids.
+func (m *UserMutation) AddGroupIDs(ids ...int) {
+	if m.groups == nil {
+		m.groups = make(map[int]struct{})
+	}
+	for i := range ids {
+		m.groups[ids[i]] = struct{}{}
+	}
+}
+
+// RemoveGroupIDs removes the groups edge to Group by ids.
+func (m *UserMutation) RemoveGroupIDs(ids ...int) {
+	if m.removedgroups == nil {
+		m.removedgroups = make(map[int]struct{})
+	}
+	for i := range ids {
+		m.removedgroups[ids[i]] = struct{}{}
+	}
+}
+
+// RemovedGroups returns the removed ids of groups.
+func (m *UserMutation) RemovedGroupsIDs() (ids []int) {
+	for id := range m.removedgroups {
+		ids = append(ids, id)
+	}
+	return
+}
+
+// GroupsIDs returns the groups ids in the mutation.
+func (m *UserMutation) GroupsIDs() (ids []int) {
+	for id := range m.groups {
+		ids = append(ids, id)
+	}
+	return
+}
+
+// ResetGroups reset all changes of the groups edge.
+func (m *UserMutation) ResetGroups() {
+	m.groups = nil
+	m.removedgroups = nil
+}
+
+// Op returns the operation name.
+func (m *UserMutation) Op() Op {
+	return m.op
+}
+
+// Type returns the node type of this mutation (User).
+func (m *UserMutation) Type() string {
+	return m.typ
+}
+
+// Fields returns all fields that were changed during
+// this mutation. Note that, in order to get all numeric
+// fields that were in/decremented, call AddedFields().
+func (m *UserMutation) Fields() []string {
+	fields := make([]string, 0, 2)
+	if m.age != nil {
+		fields = append(fields, user.FieldAge)
+	}
+	if m.name != nil {
+		fields = append(fields, user.FieldName)
+	}
+	return fields
+}
+
+// Field returns the value of a field with the given name.
+// The second boolean value indicates that this field was
+// not set, or was not define in the schema.
+func (m *UserMutation) Field(name string) (ent.Value, bool) {
+	switch name {
+	case user.FieldAge:
+		return m.Age()
+	case user.FieldName:
+		return m.Name()
+	}
+	return nil, false
+}
+
+// SetField sets the value for the given name. It returns an
+// error if the field is not defined in the schema, or if the
+// type mismatch the field type.
+func (m *UserMutation) SetField(name string, value ent.Value) error {
+	switch name {
+	case user.FieldAge:
+		v, ok := value.(int)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetAge(v)
+		return nil
+	case user.FieldName:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetName(v)
+		return nil
+	}
+	return fmt.Errorf("unknown User field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented
+// or decremented during this mutation.
+func (m *UserMutation) AddedFields() []string {
+	var fields []string
+	if m.addage != nil {
+		fields = append(fields, user.FieldAge)
+	}
+	return fields
+}
+
+// AddedField returns the numeric value that was in/decremented
+// from a field with the given name. The second value indicates
+// that this field was not set, or was not define in the schema.
+func (m *UserMutation) AddedField(name string) (ent.Value, bool) {
+	switch name {
+	case user.FieldAge:
+		return m.AddedAge()
+	}
+	return nil, false
+}
+
+// AddField adds the value for the given name. It returns an
+// error if the field is not defined in the schema, or if the
+// type mismatch the field type.
+func (m *UserMutation) AddField(name string, value ent.Value) error {
+	switch name {
+	case user.FieldAge:
+		v, ok := value.(int)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.AddAge(v)
+		return nil
+	}
+	return fmt.Errorf("unknown User numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared
+// during this mutation.
+func (m *UserMutation) ClearedFields() []string {
+	return nil
+}
+
+// FieldCleared returns a boolean indicates if this field was
+// cleared in this mutation.
+func (m *UserMutation) FieldCleared(name string) bool {
+	_, ok := m.clearedFields[name]
+	return ok
+}
+
+// ClearField clears the value for the given name. It returns an
+// error if the field is not defined in the schema.
+func (m *UserMutation) ClearField(name string) error {
+	return fmt.Errorf("unknown User nullable field %s", name)
+}
+
+// ResetField resets all changes in the mutation regarding the
+// given field name. It returns an error if the field is not
+// defined in the schema.
+func (m *UserMutation) ResetField(name string) error {
+	switch name {
+	case user.FieldAge:
+		m.ResetAge()
+		return nil
+	case user.FieldName:
+		m.ResetName()
+		return nil
+	}
+	return fmt.Errorf("unknown User field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this
+// mutation.
+func (m *UserMutation) AddedEdges() []string {
+	edges := make([]string, 0, 2)
+	if m.cars != nil {
+		edges = append(edges, user.EdgeCars)
+	}
+	if m.groups != nil {
+		edges = append(edges, user.EdgeGroups)
+	}
+	return edges
+}
+
+// AddedIDs returns all ids (to other nodes) that were added for
+// the given edge name.
+func (m *UserMutation) AddedIDs(name string) []ent.Value {
+	switch name {
+	case user.EdgeCars:
+		ids := make([]ent.Value, 0, len(m.cars))
+		for id := range m.cars {
+			ids = append(ids, id)
+		}
+		return ids
+	case user.EdgeGroups:
+		ids := make([]ent.Value, 0, len(m.groups))
+		for id := range m.groups {
+			ids = append(ids, id)
+		}
+		return ids
+	}
+	return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this
+// mutation.
+func (m *UserMutation) RemovedEdges() []string {
+	edges := make([]string, 0, 2)
+	if m.removedcars != nil {
+		edges = append(edges, user.EdgeCars)
+	}
+	if m.removedgroups != nil {
+		edges = append(edges, user.EdgeGroups)
+	}
+	return edges
+}
+
+// RemovedIDs returns all ids (to other nodes) that were removed for
+// the given edge name.
+func (m *UserMutation) RemovedIDs(name string) []ent.Value {
+	switch name {
+	case user.EdgeCars:
+		ids := make([]ent.Value, 0, len(m.removedcars))
+		for id := range m.removedcars {
+			ids = append(ids, id)
+		}
+		return ids
+	case user.EdgeGroups:
+		ids := make([]ent.Value, 0, len(m.removedgroups))
+		for id := range m.removedgroups {
+			ids = append(ids, id)
+		}
+		return ids
+	}
+	return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this
+// mutation.
+func (m *UserMutation) ClearedEdges() []string {
+	edges := make([]string, 0, 2)
+	return edges
+}
+
+// EdgeCleared returns a boolean indicates if this edge was
+// cleared in this mutation.
+func (m *UserMutation) EdgeCleared(name string) bool {
+	switch name {
+	}
+	return false
+}
+
+// ClearEdge clears the value for the given name. It returns an
+// error if the edge name is not defined in the schema.
+func (m *UserMutation) ClearEdge(name string) error {
+	switch name {
+	}
+	return fmt.Errorf("unknown User unique edge %s", name)
+}
+
+// ResetEdge resets all changes in the mutation regarding the
+// given edge name. It returns an error if the edge is not
+// defined in the schema.
+func (m *UserMutation) ResetEdge(name string) error {
+	switch name {
+	case user.EdgeCars:
+		m.ResetCars()
+		return nil
+	case user.EdgeGroups:
+		m.ResetGroups()
+		return nil
+	}
+	return fmt.Errorf("unknown User edge %s", name)
+}

+ 16 - 0
ent/predicate/predicate.go

@@ -0,0 +1,16 @@
+// Code generated by entc, DO NOT EDIT.
+
+package predicate
+
+import (
+	"github.com/facebookincubator/ent/dialect/sql"
+)
+
+// Car is the predicate function for car builders.
+type Car func(*sql.Selector)
+
+// Group is the predicate function for group builders.
+type Group func(*sql.Selector)
+
+// User is the predicate function for user builders.
+type User func(*sql.Selector)

+ 217 - 0
ent/privacy/privacy.go

@@ -0,0 +1,217 @@
+// Code generated by entc, DO NOT EDIT.
+
+package privacy
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent"
+)
+
+var (
+	// Allow may be returned by rules to indicate that the policy
+	// evaluation should terminate with an allow decision.
+	Allow = errors.New("ent/privacy: allow rule")
+
+	// Deny may be returned by rules to indicate that the policy
+	// evaluation should terminate with an deny decision.
+	Deny = errors.New("ent/privacy: deny rule")
+
+	// Skip may be returned by rules to indicate that the policy
+	// evaluation should continue to the next rule.
+	Skip = errors.New("ent/privacy: skip rule")
+)
+
+// Allowf returns an formatted wrapped Allow decision.
+func Allowf(format string, a ...interface{}) error {
+	return fmt.Errorf(format+": %w", append(a, Allow)...)
+}
+
+// Denyf returns an formatted wrapped Deny decision.
+func Denyf(format string, a ...interface{}) error {
+	return fmt.Errorf(format+": %w", append(a, Deny)...)
+}
+
+// Skipf returns an formatted wrapped Skip decision.
+func Skipf(format string, a ...interface{}) error {
+	return fmt.Errorf(format+": %w", append(a, Skip)...)
+}
+
+type (
+	// QueryPolicy combines multiple query rules into a single policy.
+	QueryPolicy []QueryRule
+
+	// QueryRule defines the interface deciding whether a
+	// query is allowed and optionally modify it.
+	QueryRule interface {
+		EvalQuery(context.Context, ent.Query) error
+	}
+)
+
+// EvalQuery evaluates a query against a query policy.
+func (policy QueryPolicy) EvalQuery(ctx context.Context, q ent.Query) error {
+	for _, rule := range policy {
+		switch err := rule.EvalQuery(ctx, q); {
+		case err == nil || errors.Is(err, Skip):
+		case errors.Is(err, Allow):
+			return nil
+		default:
+			return err
+		}
+	}
+	return nil
+}
+
+// QueryRuleFunc type is an adapter to allow the use of
+// ordinary functions as query rules.
+type QueryRuleFunc func(context.Context, ent.Query) error
+
+// Eval returns f(ctx, q).
+func (f QueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
+	return f(ctx, q)
+}
+
+type (
+	// MutationPolicy combines multiple mutation rules into a single policy.
+	MutationPolicy []MutationRule
+
+	// MutationRule defines the interface deciding whether a
+	// mutation is allowed and optionally modify it.
+	MutationRule interface {
+		EvalMutation(context.Context, ent.Mutation) error
+	}
+)
+
+// EvalMutation evaluates a mutation against a mutation policy.
+func (policy MutationPolicy) EvalMutation(ctx context.Context, m ent.Mutation) error {
+	for _, rule := range policy {
+		switch err := rule.EvalMutation(ctx, m); {
+		case err == nil || errors.Is(err, Skip):
+		case errors.Is(err, Allow):
+			return nil
+		default:
+			return err
+		}
+	}
+	return nil
+}
+
+// MutationRuleFunc type is an adapter to allow the use of
+// ordinary functions as mutation rules.
+type MutationRuleFunc func(context.Context, ent.Mutation) error
+
+// EvalMutation returns f(ctx, m).
+func (f MutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
+	return f(ctx, m)
+}
+
+// Policy groups query and mutation policies.
+type Policy struct {
+	Query    QueryPolicy
+	Mutation MutationPolicy
+}
+
+// EvalQuery forwards evaluation to query policy.
+func (policy Policy) EvalQuery(ctx context.Context, q ent.Query) error {
+	return policy.Query.EvalQuery(ctx, q)
+}
+
+// EvalMutation forwards evaluation to mutation policy.
+func (policy Policy) EvalMutation(ctx context.Context, m ent.Mutation) error {
+	return policy.Mutation.EvalMutation(ctx, m)
+}
+
+// QueryMutationRule is the interface that groups query and mutation rules.
+type QueryMutationRule interface {
+	QueryRule
+	MutationRule
+}
+
+// AlwaysAllowRule returns a rule that returns an allow decision.
+func AlwaysAllowRule() QueryMutationRule {
+	return fixedDecisionRule{Allow}
+}
+
+// AlwaysDenyRule returns a rule that returns a deny decision.
+func AlwaysDenyRule() QueryMutationRule {
+	return fixedDecisionRule{Deny}
+}
+
+type fixedDecisionRule struct{ err error }
+
+func (f fixedDecisionRule) EvalQuery(context.Context, ent.Query) error       { return f.err }
+func (f fixedDecisionRule) EvalMutation(context.Context, ent.Mutation) error { return f.err }
+
+// The CarQueryRuleFunc type is an adapter to allow the use of ordinary
+// functions as a query rule.
+type CarQueryRuleFunc func(context.Context, *ent.CarQuery) error
+
+// EvalQuery return f(ctx, q).
+func (f CarQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.CarQuery); ok {
+		return f(ctx, q)
+	}
+	return Denyf("ent/privacy: unexpected query type %T, expect *ent.CarQuery", q)
+}
+
+// The CarMutationRuleFunc type is an adapter to allow the use of ordinary
+// functions as a mutation rule.
+type CarMutationRuleFunc func(context.Context, *ent.CarMutation) error
+
+// EvalMutation calls f(ctx, m).
+func (f CarMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
+	if m, ok := m.(*ent.CarMutation); ok {
+		return f(ctx, m)
+	}
+	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.CarMutation", m)
+}
+
+// The GroupQueryRuleFunc type is an adapter to allow the use of ordinary
+// functions as a query rule.
+type GroupQueryRuleFunc func(context.Context, *ent.GroupQuery) error
+
+// EvalQuery return f(ctx, q).
+func (f GroupQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.GroupQuery); ok {
+		return f(ctx, q)
+	}
+	return Denyf("ent/privacy: unexpected query type %T, expect *ent.GroupQuery", q)
+}
+
+// The GroupMutationRuleFunc type is an adapter to allow the use of ordinary
+// functions as a mutation rule.
+type GroupMutationRuleFunc func(context.Context, *ent.GroupMutation) error
+
+// EvalMutation calls f(ctx, m).
+func (f GroupMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
+	if m, ok := m.(*ent.GroupMutation); ok {
+		return f(ctx, m)
+	}
+	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.GroupMutation", m)
+}
+
+// The UserQueryRuleFunc type is an adapter to allow the use of ordinary
+// functions as a query rule.
+type UserQueryRuleFunc func(context.Context, *ent.UserQuery) error
+
+// EvalQuery return f(ctx, q).
+func (f UserQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.UserQuery); ok {
+		return f(ctx, q)
+	}
+	return Denyf("ent/privacy: unexpected query type %T, expect *ent.UserQuery", q)
+}
+
+// The UserMutationRuleFunc type is an adapter to allow the use of ordinary
+// functions as a mutation rule.
+type UserMutationRuleFunc func(context.Context, *ent.UserMutation) error
+
+// EvalMutation calls f(ctx, m).
+func (f UserMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error {
+	if m, ok := m.(*ent.UserMutation); ok {
+		return f(ctx, m)
+	}
+	return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.UserMutation", m)
+}

+ 31 - 0
ent/runtime.go

@@ -0,0 +1,31 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/schema"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+)
+
+// The init function reads all schema descriptors with runtime
+// code (default values, validators or hooks) and stitches it
+// to their package variables.
+func init() {
+	groupFields := schema.Group{}.Fields()
+	_ = groupFields
+	// groupDescName is the schema descriptor for name field.
+	groupDescName := groupFields[0].Descriptor()
+	// group.NameValidator is a validator for the "name" field. It is called by the builders before save.
+	group.NameValidator = groupDescName.Validators[0].(func(string) error)
+	userFields := schema.User{}.Fields()
+	_ = userFields
+	// userDescAge is the schema descriptor for age field.
+	userDescAge := userFields[0].Descriptor()
+	// user.AgeValidator is a validator for the "age" field. It is called by the builders before save.
+	user.AgeValidator = userDescAge.Validators[0].(func(int) error)
+	// userDescName is the schema descriptor for name field.
+	userDescName := userFields[1].Descriptor()
+	// user.DefaultName holds the default value on creation for the name field.
+	user.DefaultName = userDescName.Default.(string)
+}

+ 10 - 0
ent/runtime/runtime.go

@@ -0,0 +1,10 @@
+// Code generated by entc, DO NOT EDIT.
+
+package runtime
+
+// The schema-stitching logic is generated in code.osinet.fr/fgm/entdemo/ent/runtime.go
+
+const (
+	Version = "v0.1.4"                                          // Version of ent codegen.
+	Sum     = "h1:1Y9icmCryGStA4e8hfNV0VS4wQkoIVZI3tzisus1jTM=" // Sum of ent codegen.
+)

+ 34 - 0
ent/schema/car.go

@@ -0,0 +1,34 @@
+package schema
+
+import (
+	"github.com/facebookincubator/ent"
+	"github.com/facebookincubator/ent/schema/edge"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// Car holds the schema definition for the Car entity.
+type Car struct {
+	ent.Schema
+}
+
+// Fields of the Car.
+func (Car) Fields() []ent.Field {
+	return []ent.Field{
+		field.String("model"),
+		field.Time("registered_at"),
+	}
+}
+
+// Edges of the Car.
+func (Car) Edges() []ent.Edge {
+	return []ent.Edge{
+		// create an inverse-edge called "owner" of type `User`
+		// and reference it to the "cars" edge (in User schema)
+		// explicitly using the `Ref` method.
+		edge.From("owner", User.Type).
+			Ref("cars").
+			// setting the edge to unique, ensure
+			// that a car can have only one owner.
+			Unique(),
+	}
+}

+ 30 - 0
ent/schema/group.go

@@ -0,0 +1,30 @@
+package schema
+
+import (
+	"regexp"
+
+	"github.com/facebookincubator/ent"
+	"github.com/facebookincubator/ent/schema/edge"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// Group holds the schema definition for the Group entity.
+type Group struct {
+	ent.Schema
+}
+
+// Fields of the Group.
+func (Group) Fields() []ent.Field {
+	return []ent.Field{
+		field.String("name").
+		// regexp validation for the group name
+		  Match(regexp.MustCompile("[a-zA-Z_]+$")),
+	}
+}
+
+// Edges of the Group.
+func (Group) Edges() []ent.Edge {
+	return []ent.Edge{
+		edge.To("users", User.Type),
+	}
+}

+ 34 - 0
ent/schema/user.go

@@ -0,0 +1,34 @@
+package schema
+
+import (
+	"github.com/facebookincubator/ent"
+	"github.com/facebookincubator/ent/schema/edge"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// User holds the schema definition for the User entity.
+type User struct {
+	ent.Schema
+}
+
+// Fields of the User.
+func (User) Fields() []ent.Field {
+	return []ent.Field{
+		field.Int("age").
+			Positive(),
+		field.String("name").
+			Default("unknown"),
+	}
+}
+
+// Edges of the User.
+func (User) Edges() []ent.Edge {
+	return []ent.Edge{
+		edge.To("cars", Car.Type),
+		// create an inverse-edge called "groups" of type `Group`
+		// and reference it to the "users" edge (in Group schema)
+		// explicitly using the `Ref` method.
+		edge.From("groups", Group.Type).
+			Ref("users"),
+	}
+}

+ 100 - 0
ent/tx.go

@@ -0,0 +1,100 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+
+	"github.com/facebookincubator/ent/dialect"
+)
+
+// Tx is a transactional client that is created by calling Client.Tx().
+type Tx struct {
+	config
+	// Car is the client for interacting with the Car builders.
+	Car *CarClient
+	// Group is the client for interacting with the Group builders.
+	Group *GroupClient
+	// User is the client for interacting with the User builders.
+	User *UserClient
+}
+
+// Commit commits the transaction.
+func (tx *Tx) Commit() error {
+	return tx.config.driver.(*txDriver).tx.Commit()
+}
+
+// Rollback rollbacks the transaction.
+func (tx *Tx) Rollback() error {
+	return tx.config.driver.(*txDriver).tx.Rollback()
+}
+
+// Client returns a Client that binds to current transaction.
+func (tx *Tx) Client() *Client {
+	client := &Client{config: tx.config}
+	client.init()
+	return client
+}
+
+func (tx *Tx) init() {
+	tx.Car = NewCarClient(tx.config)
+	tx.Group = NewGroupClient(tx.config)
+	tx.User = NewUserClient(tx.config)
+}
+
+// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation.
+// The idea is to support transactions without adding any extra code to the builders.
+// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance.
+// Commit and Rollback are nop for the internal builders and the user must call one
+// of them in order to commit or rollback the transaction.
+//
+// If a closed transaction is embedded in one of the generated entities, and the entity
+// applies a query, for example: Car.QueryXXX(), the query will be executed
+// through the driver which created this transaction.
+//
+// Note that txDriver is not goroutine safe.
+type txDriver struct {
+	// the driver we started the transaction from.
+	drv dialect.Driver
+	// tx is the underlying transaction.
+	tx dialect.Tx
+}
+
+// newTx creates a new transactional driver.
+func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) {
+	tx, err := drv.Tx(ctx)
+	if err != nil {
+		return nil, err
+	}
+	return &txDriver{tx: tx, drv: drv}, nil
+}
+
+// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls
+// from the internal builders. Should be called only by the internal builders.
+func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil }
+
+// Dialect returns the dialect of the driver we started the transaction from.
+func (tx *txDriver) Dialect() string { return tx.drv.Dialect() }
+
+// Close is a nop close.
+func (*txDriver) Close() error { return nil }
+
+// Commit is a nop commit for the internal builders.
+// User must call `Tx.Commit` in order to commit the transaction.
+func (*txDriver) Commit() error { return nil }
+
+// Rollback is a nop rollback for the internal builders.
+// User must call `Tx.Rollback` in order to rollback the transaction.
+func (*txDriver) Rollback() error { return nil }
+
+// Exec calls tx.Exec.
+func (tx *txDriver) Exec(ctx context.Context, query string, args, v interface{}) error {
+	return tx.tx.Exec(ctx, query, args, v)
+}
+
+// Query calls tx.Query.
+func (tx *txDriver) Query(ctx context.Context, query string, args, v interface{}) error {
+	return tx.tx.Query(ctx, query, args, v)
+}
+
+var _ dialect.Driver = (*txDriver)(nil)

+ 138 - 0
ent/user.go

@@ -0,0 +1,138 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"fmt"
+	"strings"
+
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+)
+
+// User is the model entity for the User schema.
+type User struct {
+	config `json:"-"`
+	// ID of the ent.
+	ID int `json:"id,omitempty"`
+	// Age holds the value of the "age" field.
+	Age int `json:"age,omitempty"`
+	// Name holds the value of the "name" field.
+	Name string `json:"name,omitempty"`
+	// Edges holds the relations/edges for other nodes in the graph.
+	// The values are being populated by the UserQuery when eager-loading is set.
+	Edges UserEdges `json:"edges"`
+}
+
+// UserEdges holds the relations/edges for other nodes in the graph.
+type UserEdges struct {
+	// Cars holds the value of the cars edge.
+	Cars []*Car
+	// Groups holds the value of the groups edge.
+	Groups []*Group
+	// loadedTypes holds the information for reporting if a
+	// type was loaded (or requested) in eager-loading or not.
+	loadedTypes [2]bool
+}
+
+// CarsOrErr returns the Cars value or an error if the edge
+// was not loaded in eager-loading.
+func (e UserEdges) CarsOrErr() ([]*Car, error) {
+	if e.loadedTypes[0] {
+		return e.Cars, nil
+	}
+	return nil, &NotLoadedError{edge: "cars"}
+}
+
+// GroupsOrErr returns the Groups value or an error if the edge
+// was not loaded in eager-loading.
+func (e UserEdges) GroupsOrErr() ([]*Group, error) {
+	if e.loadedTypes[1] {
+		return e.Groups, nil
+	}
+	return nil, &NotLoadedError{edge: "groups"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*User) scanValues() []interface{} {
+	return []interface{}{
+		&sql.NullInt64{},  // id
+		&sql.NullInt64{},  // age
+		&sql.NullString{}, // name
+	}
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the User fields.
+func (u *User) assignValues(values ...interface{}) error {
+	if m, n := len(values), len(user.Columns); m < n {
+		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+	}
+	value, ok := values[0].(*sql.NullInt64)
+	if !ok {
+		return fmt.Errorf("unexpected type %T for field id", value)
+	}
+	u.ID = int(value.Int64)
+	values = values[1:]
+	if value, ok := values[0].(*sql.NullInt64); !ok {
+		return fmt.Errorf("unexpected type %T for field age", values[0])
+	} else if value.Valid {
+		u.Age = int(value.Int64)
+	}
+	if value, ok := values[1].(*sql.NullString); !ok {
+		return fmt.Errorf("unexpected type %T for field name", values[1])
+	} else if value.Valid {
+		u.Name = value.String
+	}
+	return nil
+}
+
+// QueryCars queries the cars edge of the User.
+func (u *User) QueryCars() *CarQuery {
+	return (&UserClient{config: u.config}).QueryCars(u)
+}
+
+// QueryGroups queries the groups edge of the User.
+func (u *User) QueryGroups() *GroupQuery {
+	return (&UserClient{config: u.config}).QueryGroups(u)
+}
+
+// Update returns a builder for updating this User.
+// Note that, you need to call User.Unwrap() before calling this method, if this User
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (u *User) Update() *UserUpdateOne {
+	return (&UserClient{config: u.config}).UpdateOne(u)
+}
+
+// Unwrap unwraps the entity that was returned from a transaction after it was closed,
+// so that all next queries will be executed through the driver which created the transaction.
+func (u *User) Unwrap() *User {
+	tx, ok := u.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: User is not a transactional entity")
+	}
+	u.config.driver = tx.drv
+	return u
+}
+
+// String implements the fmt.Stringer.
+func (u *User) String() string {
+	var builder strings.Builder
+	builder.WriteString("User(")
+	builder.WriteString(fmt.Sprintf("id=%v", u.ID))
+	builder.WriteString(", age=")
+	builder.WriteString(fmt.Sprintf("%v", u.Age))
+	builder.WriteString(", name=")
+	builder.WriteString(u.Name)
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// Users is a parsable slice of User.
+type Users []*User
+
+func (u Users) config(cfg config) {
+	for _i := range u {
+		u[_i].config = cfg
+	}
+}

+ 52 - 0
ent/user/user.go

@@ -0,0 +1,52 @@
+// Code generated by entc, DO NOT EDIT.
+
+package user
+
+const (
+	// Label holds the string label denoting the user type in the database.
+	Label = "user"
+	// FieldID holds the string denoting the id field in the database.
+	FieldID   = "id"  // FieldAge holds the string denoting the age vertex property in the database.
+	FieldAge  = "age" // FieldName holds the string denoting the name vertex property in the database.
+	FieldName = "name"
+
+	// EdgeCars holds the string denoting the cars edge name in mutations.
+	EdgeCars = "cars"
+	// EdgeGroups holds the string denoting the groups edge name in mutations.
+	EdgeGroups = "groups"
+
+	// Table holds the table name of the user in the database.
+	Table = "users"
+	// CarsTable is the table the holds the cars relation/edge.
+	CarsTable = "cars"
+	// CarsInverseTable is the table name for the Car entity.
+	// It exists in this package in order to avoid circular dependency with the "car" package.
+	CarsInverseTable = "cars"
+	// CarsColumn is the table column denoting the cars relation/edge.
+	CarsColumn = "user_cars"
+	// GroupsTable is the table the holds the groups relation/edge. The primary key declared below.
+	GroupsTable = "group_users"
+	// GroupsInverseTable is the table name for the Group entity.
+	// It exists in this package in order to avoid circular dependency with the "group" package.
+	GroupsInverseTable = "groups"
+)
+
+// Columns holds all SQL columns for user fields.
+var Columns = []string{
+	FieldID,
+	FieldAge,
+	FieldName,
+}
+
+var (
+	// GroupsPrimaryKey and GroupsColumn2 are the table columns denoting the
+	// primary key for the groups relation (M2M).
+	GroupsPrimaryKey = []string{"group_id", "user_id"}
+)
+
+var (
+	// AgeValidator is a validator for the "age" field. It is called by the builders before save.
+	AgeValidator func(int) error
+	// DefaultName holds the default value on creation for the name field.
+	DefaultName string
+)

+ 381 - 0
ent/user/where.go

@@ -0,0 +1,381 @@
+// Code generated by entc, DO NOT EDIT.
+
+package user
+
+import (
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+)
+
+// ID filters vertices based on their identifier.
+func ID(id int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldID), id))
+	})
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldID), id))
+	})
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldID), id))
+	})
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(ids) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		v := make([]interface{}, len(ids))
+		for i := range v {
+			v[i] = ids[i]
+		}
+		s.Where(sql.In(s.C(FieldID), v...))
+	})
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(ids) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		v := make([]interface{}, len(ids))
+		for i := range v {
+			v[i] = ids[i]
+		}
+		s.Where(sql.NotIn(s.C(FieldID), v...))
+	})
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldID), id))
+	})
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldID), id))
+	})
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldID), id))
+	})
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldID), id))
+	})
+}
+
+// Age applies equality check predicate on the "age" field. It's identical to AgeEQ.
+func Age(v int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldAge), v))
+	})
+}
+
+// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
+func Name(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldName), v))
+	})
+}
+
+// AgeEQ applies the EQ predicate on the "age" field.
+func AgeEQ(v int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldAge), v))
+	})
+}
+
+// AgeNEQ applies the NEQ predicate on the "age" field.
+func AgeNEQ(v int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldAge), v))
+	})
+}
+
+// AgeIn applies the In predicate on the "age" field.
+func AgeIn(vs ...int) predicate.User {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.User(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldAge), v...))
+	})
+}
+
+// AgeNotIn applies the NotIn predicate on the "age" field.
+func AgeNotIn(vs ...int) predicate.User {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.User(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldAge), v...))
+	})
+}
+
+// AgeGT applies the GT predicate on the "age" field.
+func AgeGT(v int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldAge), v))
+	})
+}
+
+// AgeGTE applies the GTE predicate on the "age" field.
+func AgeGTE(v int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldAge), v))
+	})
+}
+
+// AgeLT applies the LT predicate on the "age" field.
+func AgeLT(v int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldAge), v))
+	})
+}
+
+// AgeLTE applies the LTE predicate on the "age" field.
+func AgeLTE(v int) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldAge), v))
+	})
+}
+
+// NameEQ applies the EQ predicate on the "name" field.
+func NameEQ(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.EQ(s.C(FieldName), v))
+	})
+}
+
+// NameNEQ applies the NEQ predicate on the "name" field.
+func NameNEQ(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.NEQ(s.C(FieldName), v))
+	})
+}
+
+// NameIn applies the In predicate on the "name" field.
+func NameIn(vs ...string) predicate.User {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.User(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.In(s.C(FieldName), v...))
+	})
+}
+
+// NameNotIn applies the NotIn predicate on the "name" field.
+func NameNotIn(vs ...string) predicate.User {
+	v := make([]interface{}, len(vs))
+	for i := range v {
+		v[i] = vs[i]
+	}
+	return predicate.User(func(s *sql.Selector) {
+		// if not arguments were provided, append the FALSE constants,
+		// since we can't apply "IN ()". This will make this predicate falsy.
+		if len(vs) == 0 {
+			s.Where(sql.False())
+			return
+		}
+		s.Where(sql.NotIn(s.C(FieldName), v...))
+	})
+}
+
+// NameGT applies the GT predicate on the "name" field.
+func NameGT(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.GT(s.C(FieldName), v))
+	})
+}
+
+// NameGTE applies the GTE predicate on the "name" field.
+func NameGTE(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.GTE(s.C(FieldName), v))
+	})
+}
+
+// NameLT applies the LT predicate on the "name" field.
+func NameLT(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.LT(s.C(FieldName), v))
+	})
+}
+
+// NameLTE applies the LTE predicate on the "name" field.
+func NameLTE(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.LTE(s.C(FieldName), v))
+	})
+}
+
+// NameContains applies the Contains predicate on the "name" field.
+func NameContains(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.Contains(s.C(FieldName), v))
+	})
+}
+
+// NameHasPrefix applies the HasPrefix predicate on the "name" field.
+func NameHasPrefix(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.HasPrefix(s.C(FieldName), v))
+	})
+}
+
+// NameHasSuffix applies the HasSuffix predicate on the "name" field.
+func NameHasSuffix(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.HasSuffix(s.C(FieldName), v))
+	})
+}
+
+// NameEqualFold applies the EqualFold predicate on the "name" field.
+func NameEqualFold(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.EqualFold(s.C(FieldName), v))
+	})
+}
+
+// NameContainsFold applies the ContainsFold predicate on the "name" field.
+func NameContainsFold(v string) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s.Where(sql.ContainsFold(s.C(FieldName), v))
+	})
+}
+
+// HasCars applies the HasEdge predicate on the "cars" edge.
+func HasCars() predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(CarsTable, FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, CarsTable, CarsColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasCarsWith applies the HasEdge predicate on the "cars" edge with a given conditions (other predicates).
+func HasCarsWith(preds ...predicate.Car) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(CarsInverseTable, FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, CarsTable, CarsColumn),
+		)
+		sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+			for _, p := range preds {
+				p(s)
+			}
+		})
+	})
+}
+
+// HasGroups applies the HasEdge predicate on the "groups" edge.
+func HasGroups() predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(GroupsTable, FieldID),
+			sqlgraph.Edge(sqlgraph.M2M, true, GroupsTable, GroupsPrimaryKey...),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasGroupsWith applies the HasEdge predicate on the "groups" edge with a given conditions (other predicates).
+func HasGroupsWith(preds ...predicate.Group) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.To(GroupsInverseTable, FieldID),
+			sqlgraph.Edge(sqlgraph.M2M, true, GroupsTable, GroupsPrimaryKey...),
+		)
+		sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+			for _, p := range preds {
+				p(s)
+			}
+		})
+	})
+}
+
+// And groups list of predicates with the AND operator between them.
+func And(predicates ...predicate.User) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s1 := s.Clone().SetP(nil)
+		for _, p := range predicates {
+			p(s1)
+		}
+		s.Where(s1.P())
+	})
+}
+
+// Or groups list of predicates with the OR operator between them.
+func Or(predicates ...predicate.User) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		s1 := s.Clone().SetP(nil)
+		for i, p := range predicates {
+			if i > 0 {
+				s1.Or()
+			}
+			p(s1)
+		}
+		s.Where(s1.P())
+	})
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.User) predicate.User {
+	return predicate.User(func(s *sql.Selector) {
+		p(s.Not())
+	})
+}

+ 197 - 0
ent/user_create.go

@@ -0,0 +1,197 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// UserCreate is the builder for creating a User entity.
+type UserCreate struct {
+	config
+	mutation *UserMutation
+	hooks    []Hook
+}
+
+// SetAge sets the age field.
+func (uc *UserCreate) SetAge(i int) *UserCreate {
+	uc.mutation.SetAge(i)
+	return uc
+}
+
+// SetName sets the name field.
+func (uc *UserCreate) SetName(s string) *UserCreate {
+	uc.mutation.SetName(s)
+	return uc
+}
+
+// SetNillableName sets the name field if the given value is not nil.
+func (uc *UserCreate) SetNillableName(s *string) *UserCreate {
+	if s != nil {
+		uc.SetName(*s)
+	}
+	return uc
+}
+
+// AddCarIDs adds the cars edge to Car by ids.
+func (uc *UserCreate) AddCarIDs(ids ...int) *UserCreate {
+	uc.mutation.AddCarIDs(ids...)
+	return uc
+}
+
+// AddCars adds the cars edges to Car.
+func (uc *UserCreate) AddCars(c ...*Car) *UserCreate {
+	ids := make([]int, len(c))
+	for i := range c {
+		ids[i] = c[i].ID
+	}
+	return uc.AddCarIDs(ids...)
+}
+
+// AddGroupIDs adds the groups edge to Group by ids.
+func (uc *UserCreate) AddGroupIDs(ids ...int) *UserCreate {
+	uc.mutation.AddGroupIDs(ids...)
+	return uc
+}
+
+// AddGroups adds the groups edges to Group.
+func (uc *UserCreate) AddGroups(g ...*Group) *UserCreate {
+	ids := make([]int, len(g))
+	for i := range g {
+		ids[i] = g[i].ID
+	}
+	return uc.AddGroupIDs(ids...)
+}
+
+// Save creates the User in the database.
+func (uc *UserCreate) Save(ctx context.Context) (*User, error) {
+	if _, ok := uc.mutation.Age(); !ok {
+		return nil, errors.New("ent: missing required field \"age\"")
+	}
+	if v, ok := uc.mutation.Age(); ok {
+		if err := user.AgeValidator(v); err != nil {
+			return nil, fmt.Errorf("ent: validator failed for field \"age\": %v", err)
+		}
+	}
+	if _, ok := uc.mutation.Name(); !ok {
+		v := user.DefaultName
+		uc.mutation.SetName(v)
+	}
+	var (
+		err  error
+		node *User
+	)
+	if len(uc.hooks) == 0 {
+		node, err = uc.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*UserMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			uc.mutation = mutation
+			node, err = uc.sqlSave(ctx)
+			return node, err
+		})
+		for i := len(uc.hooks) - 1; i >= 0; i-- {
+			mut = uc.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, uc.mutation); err != nil {
+			return nil, err
+		}
+	}
+	return node, err
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (uc *UserCreate) SaveX(ctx context.Context) *User {
+	v, err := uc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) {
+	var (
+		u     = &User{config: uc.config}
+		_spec = &sqlgraph.CreateSpec{
+			Table: user.Table,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: user.FieldID,
+			},
+		}
+	)
+	if value, ok := uc.mutation.Age(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt,
+			Value:  value,
+			Column: user.FieldAge,
+		})
+		u.Age = value
+	}
+	if value, ok := uc.mutation.Name(); ok {
+		_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: user.FieldName,
+		})
+		u.Name = value
+	}
+	if nodes := uc.mutation.CarsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   user.CarsTable,
+			Columns: []string{user.CarsColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: car.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges = append(_spec.Edges, edge)
+	}
+	if nodes := uc.mutation.GroupsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: true,
+			Table:   user.GroupsTable,
+			Columns: user.GroupsPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: group.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges = append(_spec.Edges, edge)
+	}
+	if err := sqlgraph.CreateNode(ctx, uc.driver, _spec); err != nil {
+		if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return nil, err
+	}
+	id := _spec.ID.Value.(int64)
+	u.ID = int(id)
+	return u, nil
+}

+ 108 - 0
ent/user_delete.go

@@ -0,0 +1,108 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// UserDelete is the builder for deleting a User entity.
+type UserDelete struct {
+	config
+	hooks      []Hook
+	mutation   *UserMutation
+	predicates []predicate.User
+}
+
+// Where adds a new predicate to the delete builder.
+func (ud *UserDelete) Where(ps ...predicate.User) *UserDelete {
+	ud.predicates = append(ud.predicates, ps...)
+	return ud
+}
+
+// Exec executes the deletion query and returns how many vertices were deleted.
+func (ud *UserDelete) Exec(ctx context.Context) (int, error) {
+	var (
+		err      error
+		affected int
+	)
+	if len(ud.hooks) == 0 {
+		affected, err = ud.sqlExec(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*UserMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			ud.mutation = mutation
+			affected, err = ud.sqlExec(ctx)
+			return affected, err
+		})
+		for i := len(ud.hooks) - 1; i >= 0; i-- {
+			mut = ud.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, ud.mutation); err != nil {
+			return 0, err
+		}
+	}
+	return affected, err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (ud *UserDelete) ExecX(ctx context.Context) int {
+	n, err := ud.Exec(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return n
+}
+
+func (ud *UserDelete) sqlExec(ctx context.Context) (int, error) {
+	_spec := &sqlgraph.DeleteSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table: user.Table,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: user.FieldID,
+			},
+		},
+	}
+	if ps := ud.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return sqlgraph.DeleteNodes(ctx, ud.driver, _spec)
+}
+
+// UserDeleteOne is the builder for deleting a single User entity.
+type UserDeleteOne struct {
+	ud *UserDelete
+}
+
+// Exec executes the deletion query.
+func (udo *UserDeleteOne) Exec(ctx context.Context) error {
+	n, err := udo.ud.Exec(ctx)
+	switch {
+	case err != nil:
+		return err
+	case n == 0:
+		return &NotFoundError{user.Label}
+	default:
+		return nil
+	}
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (udo *UserDeleteOne) ExecX(ctx context.Context) {
+	udo.ud.ExecX(ctx)
+}

+ 763 - 0
ent/user_query.go

@@ -0,0 +1,763 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"database/sql/driver"
+	"errors"
+	"fmt"
+	"math"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// UserQuery is the builder for querying User entities.
+type UserQuery struct {
+	config
+	limit      *int
+	offset     *int
+	order      []Order
+	unique     []string
+	predicates []predicate.User
+	// eager-loading edges.
+	withCars   *CarQuery
+	withGroups *GroupQuery
+	// intermediate query.
+	sql *sql.Selector
+}
+
+// Where adds a new predicate for the builder.
+func (uq *UserQuery) Where(ps ...predicate.User) *UserQuery {
+	uq.predicates = append(uq.predicates, ps...)
+	return uq
+}
+
+// Limit adds a limit step to the query.
+func (uq *UserQuery) Limit(limit int) *UserQuery {
+	uq.limit = &limit
+	return uq
+}
+
+// Offset adds an offset step to the query.
+func (uq *UserQuery) Offset(offset int) *UserQuery {
+	uq.offset = &offset
+	return uq
+}
+
+// Order adds an order step to the query.
+func (uq *UserQuery) Order(o ...Order) *UserQuery {
+	uq.order = append(uq.order, o...)
+	return uq
+}
+
+// QueryCars chains the current query on the cars edge.
+func (uq *UserQuery) QueryCars() *CarQuery {
+	query := &CarQuery{config: uq.config}
+	step := sqlgraph.NewStep(
+		sqlgraph.From(user.Table, user.FieldID, uq.sqlQuery()),
+		sqlgraph.To(car.Table, car.FieldID),
+		sqlgraph.Edge(sqlgraph.O2M, false, user.CarsTable, user.CarsColumn),
+	)
+	query.sql = sqlgraph.SetNeighbors(uq.driver.Dialect(), step)
+	return query
+}
+
+// QueryGroups chains the current query on the groups edge.
+func (uq *UserQuery) QueryGroups() *GroupQuery {
+	query := &GroupQuery{config: uq.config}
+	step := sqlgraph.NewStep(
+		sqlgraph.From(user.Table, user.FieldID, uq.sqlQuery()),
+		sqlgraph.To(group.Table, group.FieldID),
+		sqlgraph.Edge(sqlgraph.M2M, true, user.GroupsTable, user.GroupsPrimaryKey...),
+	)
+	query.sql = sqlgraph.SetNeighbors(uq.driver.Dialect(), step)
+	return query
+}
+
+// First returns the first User entity in the query. Returns *NotFoundError when no user was found.
+func (uq *UserQuery) First(ctx context.Context) (*User, error) {
+	us, err := uq.Limit(1).All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	if len(us) == 0 {
+		return nil, &NotFoundError{user.Label}
+	}
+	return us[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (uq *UserQuery) FirstX(ctx context.Context) *User {
+	u, err := uq.First(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return u
+}
+
+// FirstID returns the first User id in the query. Returns *NotFoundError when no id was found.
+func (uq *UserQuery) FirstID(ctx context.Context) (id int, err error) {
+	var ids []int
+	if ids, err = uq.Limit(1).IDs(ctx); err != nil {
+		return
+	}
+	if len(ids) == 0 {
+		err = &NotFoundError{user.Label}
+		return
+	}
+	return ids[0], nil
+}
+
+// FirstXID is like FirstID, but panics if an error occurs.
+func (uq *UserQuery) FirstXID(ctx context.Context) int {
+	id, err := uq.FirstID(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return id
+}
+
+// Only returns the only User entity in the query, returns an error if not exactly one entity was returned.
+func (uq *UserQuery) Only(ctx context.Context) (*User, error) {
+	us, err := uq.Limit(2).All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	switch len(us) {
+	case 1:
+		return us[0], nil
+	case 0:
+		return nil, &NotFoundError{user.Label}
+	default:
+		return nil, &NotSingularError{user.Label}
+	}
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (uq *UserQuery) OnlyX(ctx context.Context) *User {
+	u, err := uq.Only(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return u
+}
+
+// OnlyID returns the only User id in the query, returns an error if not exactly one id was returned.
+func (uq *UserQuery) OnlyID(ctx context.Context) (id int, err error) {
+	var ids []int
+	if ids, err = uq.Limit(2).IDs(ctx); err != nil {
+		return
+	}
+	switch len(ids) {
+	case 1:
+		id = ids[0]
+	case 0:
+		err = &NotFoundError{user.Label}
+	default:
+		err = &NotSingularError{user.Label}
+	}
+	return
+}
+
+// OnlyXID is like OnlyID, but panics if an error occurs.
+func (uq *UserQuery) OnlyXID(ctx context.Context) int {
+	id, err := uq.OnlyID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// All executes the query and returns a list of Users.
+func (uq *UserQuery) All(ctx context.Context) ([]*User, error) {
+	return uq.sqlAll(ctx)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (uq *UserQuery) AllX(ctx context.Context) []*User {
+	us, err := uq.All(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return us
+}
+
+// IDs executes the query and returns a list of User ids.
+func (uq *UserQuery) IDs(ctx context.Context) ([]int, error) {
+	var ids []int
+	if err := uq.Select(user.FieldID).Scan(ctx, &ids); err != nil {
+		return nil, err
+	}
+	return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (uq *UserQuery) IDsX(ctx context.Context) []int {
+	ids, err := uq.IDs(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return ids
+}
+
+// Count returns the count of the given query.
+func (uq *UserQuery) Count(ctx context.Context) (int, error) {
+	return uq.sqlCount(ctx)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (uq *UserQuery) CountX(ctx context.Context) int {
+	count, err := uq.Count(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (uq *UserQuery) Exist(ctx context.Context) (bool, error) {
+	return uq.sqlExist(ctx)
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (uq *UserQuery) ExistX(ctx context.Context) bool {
+	exist, err := uq.Exist(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return exist
+}
+
+// Clone returns a duplicate of the query builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (uq *UserQuery) Clone() *UserQuery {
+	return &UserQuery{
+		config:     uq.config,
+		limit:      uq.limit,
+		offset:     uq.offset,
+		order:      append([]Order{}, uq.order...),
+		unique:     append([]string{}, uq.unique...),
+		predicates: append([]predicate.User{}, uq.predicates...),
+		// clone intermediate query.
+		sql: uq.sql.Clone(),
+	}
+}
+
+//  WithCars tells the query-builder to eager-loads the nodes that are connected to
+// the "cars" edge. The optional arguments used to configure the query builder of the edge.
+func (uq *UserQuery) WithCars(opts ...func(*CarQuery)) *UserQuery {
+	query := &CarQuery{config: uq.config}
+	for _, opt := range opts {
+		opt(query)
+	}
+	uq.withCars = query
+	return uq
+}
+
+//  WithGroups tells the query-builder to eager-loads the nodes that are connected to
+// the "groups" edge. The optional arguments used to configure the query builder of the edge.
+func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery {
+	query := &GroupQuery{config: uq.config}
+	for _, opt := range opts {
+		opt(query)
+	}
+	uq.withGroups = query
+	return uq
+}
+
+// GroupBy used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+//	var v []struct {
+//		Age int `json:"age,omitempty"`
+//		Count int `json:"count,omitempty"`
+//	}
+//
+//	client.User.Query().
+//		GroupBy(user.FieldAge).
+//		Aggregate(ent.Count()).
+//		Scan(ctx, &v)
+//
+func (uq *UserQuery) GroupBy(field string, fields ...string) *UserGroupBy {
+	group := &UserGroupBy{config: uq.config}
+	group.fields = append([]string{field}, fields...)
+	group.sql = uq.sqlQuery()
+	return group
+}
+
+// Select one or more fields from the given query.
+//
+// Example:
+//
+//	var v []struct {
+//		Age int `json:"age,omitempty"`
+//	}
+//
+//	client.User.Query().
+//		Select(user.FieldAge).
+//		Scan(ctx, &v)
+//
+func (uq *UserQuery) Select(field string, fields ...string) *UserSelect {
+	selector := &UserSelect{config: uq.config}
+	selector.fields = append([]string{field}, fields...)
+	selector.sql = uq.sqlQuery()
+	return selector
+}
+
+func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) {
+	var (
+		nodes       = []*User{}
+		_spec       = uq.querySpec()
+		loadedTypes = [2]bool{
+			uq.withCars != nil,
+			uq.withGroups != nil,
+		}
+	)
+	_spec.ScanValues = func() []interface{} {
+		node := &User{config: uq.config}
+		nodes = append(nodes, node)
+		values := node.scanValues()
+		return values
+	}
+	_spec.Assign = func(values ...interface{}) error {
+		if len(nodes) == 0 {
+			return fmt.Errorf("ent: Assign called without calling ScanValues")
+		}
+		node := nodes[len(nodes)-1]
+		node.Edges.loadedTypes = loadedTypes
+		return node.assignValues(values...)
+	}
+	if err := sqlgraph.QueryNodes(ctx, uq.driver, _spec); err != nil {
+		return nil, err
+	}
+	if len(nodes) == 0 {
+		return nodes, nil
+	}
+
+	if query := uq.withCars; query != nil {
+		fks := make([]driver.Value, 0, len(nodes))
+		nodeids := make(map[int]*User)
+		for i := range nodes {
+			fks = append(fks, nodes[i].ID)
+			nodeids[nodes[i].ID] = nodes[i]
+		}
+		query.withFKs = true
+		query.Where(predicate.Car(func(s *sql.Selector) {
+			s.Where(sql.InValues(user.CarsColumn, fks...))
+		}))
+		neighbors, err := query.All(ctx)
+		if err != nil {
+			return nil, err
+		}
+		for _, n := range neighbors {
+			fk := n.user_cars
+			if fk == nil {
+				return nil, fmt.Errorf(`foreign-key "user_cars" is nil for node %v`, n.ID)
+			}
+			node, ok := nodeids[*fk]
+			if !ok {
+				return nil, fmt.Errorf(`unexpected foreign-key "user_cars" returned %v for node %v`, *fk, n.ID)
+			}
+			node.Edges.Cars = append(node.Edges.Cars, n)
+		}
+	}
+
+	if query := uq.withGroups; query != nil {
+		fks := make([]driver.Value, 0, len(nodes))
+		ids := make(map[int]*User, len(nodes))
+		for _, node := range nodes {
+			ids[node.ID] = node
+			fks = append(fks, node.ID)
+		}
+		var (
+			edgeids []int
+			edges   = make(map[int][]*User)
+		)
+		_spec := &sqlgraph.EdgeQuerySpec{
+			Edge: &sqlgraph.EdgeSpec{
+				Inverse: true,
+				Table:   user.GroupsTable,
+				Columns: user.GroupsPrimaryKey,
+			},
+			Predicate: func(s *sql.Selector) {
+				s.Where(sql.InValues(user.GroupsPrimaryKey[1], fks...))
+			},
+
+			ScanValues: func() [2]interface{} {
+				return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}}
+			},
+			Assign: func(out, in interface{}) error {
+				eout, ok := out.(*sql.NullInt64)
+				if !ok || eout == nil {
+					return fmt.Errorf("unexpected id value for edge-out")
+				}
+				ein, ok := in.(*sql.NullInt64)
+				if !ok || ein == nil {
+					return fmt.Errorf("unexpected id value for edge-in")
+				}
+				outValue := int(eout.Int64)
+				inValue := int(ein.Int64)
+				node, ok := ids[outValue]
+				if !ok {
+					return fmt.Errorf("unexpected node id in edges: %v", outValue)
+				}
+				edgeids = append(edgeids, inValue)
+				edges[inValue] = append(edges[inValue], node)
+				return nil
+			},
+		}
+		if err := sqlgraph.QueryEdges(ctx, uq.driver, _spec); err != nil {
+			return nil, fmt.Errorf(`query edges "groups": %v`, err)
+		}
+		query.Where(group.IDIn(edgeids...))
+		neighbors, err := query.All(ctx)
+		if err != nil {
+			return nil, err
+		}
+		for _, n := range neighbors {
+			nodes, ok := edges[n.ID]
+			if !ok {
+				return nil, fmt.Errorf(`unexpected "groups" node returned %v`, n.ID)
+			}
+			for i := range nodes {
+				nodes[i].Edges.Groups = append(nodes[i].Edges.Groups, n)
+			}
+		}
+	}
+
+	return nodes, nil
+}
+
+func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) {
+	_spec := uq.querySpec()
+	return sqlgraph.CountNodes(ctx, uq.driver, _spec)
+}
+
+func (uq *UserQuery) sqlExist(ctx context.Context) (bool, error) {
+	n, err := uq.sqlCount(ctx)
+	if err != nil {
+		return false, fmt.Errorf("ent: check existence: %v", err)
+	}
+	return n > 0, nil
+}
+
+func (uq *UserQuery) querySpec() *sqlgraph.QuerySpec {
+	_spec := &sqlgraph.QuerySpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   user.Table,
+			Columns: user.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: user.FieldID,
+			},
+		},
+		From:   uq.sql,
+		Unique: true,
+	}
+	if ps := uq.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if limit := uq.limit; limit != nil {
+		_spec.Limit = *limit
+	}
+	if offset := uq.offset; offset != nil {
+		_spec.Offset = *offset
+	}
+	if ps := uq.order; len(ps) > 0 {
+		_spec.Order = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return _spec
+}
+
+func (uq *UserQuery) sqlQuery() *sql.Selector {
+	builder := sql.Dialect(uq.driver.Dialect())
+	t1 := builder.Table(user.Table)
+	selector := builder.Select(t1.Columns(user.Columns...)...).From(t1)
+	if uq.sql != nil {
+		selector = uq.sql
+		selector.Select(selector.Columns(user.Columns...)...)
+	}
+	for _, p := range uq.predicates {
+		p(selector)
+	}
+	for _, p := range uq.order {
+		p(selector)
+	}
+	if offset := uq.offset; offset != nil {
+		// limit is mandatory for offset clause. We start
+		// with default value, and override it below if needed.
+		selector.Offset(*offset).Limit(math.MaxInt32)
+	}
+	if limit := uq.limit; limit != nil {
+		selector.Limit(*limit)
+	}
+	return selector
+}
+
+// UserGroupBy is the builder for group-by User entities.
+type UserGroupBy struct {
+	config
+	fields []string
+	fns    []Aggregate
+	// intermediate query.
+	sql *sql.Selector
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (ugb *UserGroupBy) Aggregate(fns ...Aggregate) *UserGroupBy {
+	ugb.fns = append(ugb.fns, fns...)
+	return ugb
+}
+
+// Scan applies the group-by query and scan the result into the given value.
+func (ugb *UserGroupBy) Scan(ctx context.Context, v interface{}) error {
+	return ugb.sqlScan(ctx, v)
+}
+
+// ScanX is like Scan, but panics if an error occurs.
+func (ugb *UserGroupBy) ScanX(ctx context.Context, v interface{}) {
+	if err := ugb.Scan(ctx, v); err != nil {
+		panic(err)
+	}
+}
+
+// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
+func (ugb *UserGroupBy) Strings(ctx context.Context) ([]string, error) {
+	if len(ugb.fields) > 1 {
+		return nil, errors.New("ent: UserGroupBy.Strings is not achievable when grouping more than 1 field")
+	}
+	var v []string
+	if err := ugb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// StringsX is like Strings, but panics if an error occurs.
+func (ugb *UserGroupBy) StringsX(ctx context.Context) []string {
+	v, err := ugb.Strings(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
+func (ugb *UserGroupBy) Ints(ctx context.Context) ([]int, error) {
+	if len(ugb.fields) > 1 {
+		return nil, errors.New("ent: UserGroupBy.Ints is not achievable when grouping more than 1 field")
+	}
+	var v []int
+	if err := ugb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// IntsX is like Ints, but panics if an error occurs.
+func (ugb *UserGroupBy) IntsX(ctx context.Context) []int {
+	v, err := ugb.Ints(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
+func (ugb *UserGroupBy) Float64s(ctx context.Context) ([]float64, error) {
+	if len(ugb.fields) > 1 {
+		return nil, errors.New("ent: UserGroupBy.Float64s is not achievable when grouping more than 1 field")
+	}
+	var v []float64
+	if err := ugb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// Float64sX is like Float64s, but panics if an error occurs.
+func (ugb *UserGroupBy) Float64sX(ctx context.Context) []float64 {
+	v, err := ugb.Float64s(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
+func (ugb *UserGroupBy) Bools(ctx context.Context) ([]bool, error) {
+	if len(ugb.fields) > 1 {
+		return nil, errors.New("ent: UserGroupBy.Bools is not achievable when grouping more than 1 field")
+	}
+	var v []bool
+	if err := ugb.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// BoolsX is like Bools, but panics if an error occurs.
+func (ugb *UserGroupBy) BoolsX(ctx context.Context) []bool {
+	v, err := ugb.Bools(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error {
+	rows := &sql.Rows{}
+	query, args := ugb.sqlQuery().Query()
+	if err := ugb.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+func (ugb *UserGroupBy) sqlQuery() *sql.Selector {
+	selector := ugb.sql
+	columns := make([]string, 0, len(ugb.fields)+len(ugb.fns))
+	columns = append(columns, ugb.fields...)
+	for _, fn := range ugb.fns {
+		columns = append(columns, fn(selector))
+	}
+	return selector.Select(columns...).GroupBy(ugb.fields...)
+}
+
+// UserSelect is the builder for select fields of User entities.
+type UserSelect struct {
+	config
+	fields []string
+	// intermediate queries.
+	sql *sql.Selector
+}
+
+// Scan applies the selector query and scan the result into the given value.
+func (us *UserSelect) Scan(ctx context.Context, v interface{}) error {
+	return us.sqlScan(ctx, v)
+}
+
+// ScanX is like Scan, but panics if an error occurs.
+func (us *UserSelect) ScanX(ctx context.Context, v interface{}) {
+	if err := us.Scan(ctx, v); err != nil {
+		panic(err)
+	}
+}
+
+// Strings returns list of strings from selector. It is only allowed when selecting one field.
+func (us *UserSelect) Strings(ctx context.Context) ([]string, error) {
+	if len(us.fields) > 1 {
+		return nil, errors.New("ent: UserSelect.Strings is not achievable when selecting more than 1 field")
+	}
+	var v []string
+	if err := us.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// StringsX is like Strings, but panics if an error occurs.
+func (us *UserSelect) StringsX(ctx context.Context) []string {
+	v, err := us.Strings(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Ints returns list of ints from selector. It is only allowed when selecting one field.
+func (us *UserSelect) Ints(ctx context.Context) ([]int, error) {
+	if len(us.fields) > 1 {
+		return nil, errors.New("ent: UserSelect.Ints is not achievable when selecting more than 1 field")
+	}
+	var v []int
+	if err := us.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// IntsX is like Ints, but panics if an error occurs.
+func (us *UserSelect) IntsX(ctx context.Context) []int {
+	v, err := us.Ints(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Float64s returns list of float64s from selector. It is only allowed when selecting one field.
+func (us *UserSelect) Float64s(ctx context.Context) ([]float64, error) {
+	if len(us.fields) > 1 {
+		return nil, errors.New("ent: UserSelect.Float64s is not achievable when selecting more than 1 field")
+	}
+	var v []float64
+	if err := us.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// Float64sX is like Float64s, but panics if an error occurs.
+func (us *UserSelect) Float64sX(ctx context.Context) []float64 {
+	v, err := us.Float64s(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Bools returns list of bools from selector. It is only allowed when selecting one field.
+func (us *UserSelect) Bools(ctx context.Context) ([]bool, error) {
+	if len(us.fields) > 1 {
+		return nil, errors.New("ent: UserSelect.Bools is not achievable when selecting more than 1 field")
+	}
+	var v []bool
+	if err := us.Scan(ctx, &v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+// BoolsX is like Bools, but panics if an error occurs.
+func (us *UserSelect) BoolsX(ctx context.Context) []bool {
+	v, err := us.Bools(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+func (us *UserSelect) sqlScan(ctx context.Context, v interface{}) error {
+	rows := &sql.Rows{}
+	query, args := us.sqlQuery().Query()
+	if err := us.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+func (us *UserSelect) sqlQuery() sql.Querier {
+	selector := us.sql
+	selector.Select(selector.Columns(us.fields...)...)
+	return selector
+}

+ 576 - 0
ent/user_update.go

@@ -0,0 +1,576 @@
+// Code generated by entc, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/predicate"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+	"github.com/facebookincubator/ent/dialect/sql"
+	"github.com/facebookincubator/ent/dialect/sql/sqlgraph"
+	"github.com/facebookincubator/ent/schema/field"
+)
+
+// UserUpdate is the builder for updating User entities.
+type UserUpdate struct {
+	config
+	hooks      []Hook
+	mutation   *UserMutation
+	predicates []predicate.User
+}
+
+// Where adds a new predicate for the builder.
+func (uu *UserUpdate) Where(ps ...predicate.User) *UserUpdate {
+	uu.predicates = append(uu.predicates, ps...)
+	return uu
+}
+
+// SetAge sets the age field.
+func (uu *UserUpdate) SetAge(i int) *UserUpdate {
+	uu.mutation.ResetAge()
+	uu.mutation.SetAge(i)
+	return uu
+}
+
+// AddAge adds i to age.
+func (uu *UserUpdate) AddAge(i int) *UserUpdate {
+	uu.mutation.AddAge(i)
+	return uu
+}
+
+// SetName sets the name field.
+func (uu *UserUpdate) SetName(s string) *UserUpdate {
+	uu.mutation.SetName(s)
+	return uu
+}
+
+// SetNillableName sets the name field if the given value is not nil.
+func (uu *UserUpdate) SetNillableName(s *string) *UserUpdate {
+	if s != nil {
+		uu.SetName(*s)
+	}
+	return uu
+}
+
+// AddCarIDs adds the cars edge to Car by ids.
+func (uu *UserUpdate) AddCarIDs(ids ...int) *UserUpdate {
+	uu.mutation.AddCarIDs(ids...)
+	return uu
+}
+
+// AddCars adds the cars edges to Car.
+func (uu *UserUpdate) AddCars(c ...*Car) *UserUpdate {
+	ids := make([]int, len(c))
+	for i := range c {
+		ids[i] = c[i].ID
+	}
+	return uu.AddCarIDs(ids...)
+}
+
+// AddGroupIDs adds the groups edge to Group by ids.
+func (uu *UserUpdate) AddGroupIDs(ids ...int) *UserUpdate {
+	uu.mutation.AddGroupIDs(ids...)
+	return uu
+}
+
+// AddGroups adds the groups edges to Group.
+func (uu *UserUpdate) AddGroups(g ...*Group) *UserUpdate {
+	ids := make([]int, len(g))
+	for i := range g {
+		ids[i] = g[i].ID
+	}
+	return uu.AddGroupIDs(ids...)
+}
+
+// RemoveCarIDs removes the cars edge to Car by ids.
+func (uu *UserUpdate) RemoveCarIDs(ids ...int) *UserUpdate {
+	uu.mutation.RemoveCarIDs(ids...)
+	return uu
+}
+
+// RemoveCars removes cars edges to Car.
+func (uu *UserUpdate) RemoveCars(c ...*Car) *UserUpdate {
+	ids := make([]int, len(c))
+	for i := range c {
+		ids[i] = c[i].ID
+	}
+	return uu.RemoveCarIDs(ids...)
+}
+
+// RemoveGroupIDs removes the groups edge to Group by ids.
+func (uu *UserUpdate) RemoveGroupIDs(ids ...int) *UserUpdate {
+	uu.mutation.RemoveGroupIDs(ids...)
+	return uu
+}
+
+// RemoveGroups removes groups edges to Group.
+func (uu *UserUpdate) RemoveGroups(g ...*Group) *UserUpdate {
+	ids := make([]int, len(g))
+	for i := range g {
+		ids[i] = g[i].ID
+	}
+	return uu.RemoveGroupIDs(ids...)
+}
+
+// Save executes the query and returns the number of rows/vertices matched by this operation.
+func (uu *UserUpdate) Save(ctx context.Context) (int, error) {
+	if v, ok := uu.mutation.Age(); ok {
+		if err := user.AgeValidator(v); err != nil {
+			return 0, fmt.Errorf("ent: validator failed for field \"age\": %v", err)
+		}
+	}
+
+	var (
+		err      error
+		affected int
+	)
+	if len(uu.hooks) == 0 {
+		affected, err = uu.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*UserMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			uu.mutation = mutation
+			affected, err = uu.sqlSave(ctx)
+			return affected, err
+		})
+		for i := len(uu.hooks) - 1; i >= 0; i-- {
+			mut = uu.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, uu.mutation); err != nil {
+			return 0, err
+		}
+	}
+	return affected, err
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (uu *UserUpdate) SaveX(ctx context.Context) int {
+	affected, err := uu.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (uu *UserUpdate) Exec(ctx context.Context) error {
+	_, err := uu.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (uu *UserUpdate) ExecX(ctx context.Context) {
+	if err := uu.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	_spec := &sqlgraph.UpdateSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   user.Table,
+			Columns: user.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: user.FieldID,
+			},
+		},
+	}
+	if ps := uu.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := uu.mutation.Age(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt,
+			Value:  value,
+			Column: user.FieldAge,
+		})
+	}
+	if value, ok := uu.mutation.AddedAge(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt,
+			Value:  value,
+			Column: user.FieldAge,
+		})
+	}
+	if value, ok := uu.mutation.Name(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: user.FieldName,
+		})
+	}
+	if nodes := uu.mutation.RemovedCarsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   user.CarsTable,
+			Columns: []string{user.CarsColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: car.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := uu.mutation.CarsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   user.CarsTable,
+			Columns: []string{user.CarsColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: car.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if nodes := uu.mutation.RemovedGroupsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: true,
+			Table:   user.GroupsTable,
+			Columns: user.GroupsPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: group.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := uu.mutation.GroupsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: true,
+			Table:   user.GroupsTable,
+			Columns: user.GroupsPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: group.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{user.Label}
+		} else if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return 0, err
+	}
+	return n, nil
+}
+
+// UserUpdateOne is the builder for updating a single User entity.
+type UserUpdateOne struct {
+	config
+	hooks    []Hook
+	mutation *UserMutation
+}
+
+// SetAge sets the age field.
+func (uuo *UserUpdateOne) SetAge(i int) *UserUpdateOne {
+	uuo.mutation.ResetAge()
+	uuo.mutation.SetAge(i)
+	return uuo
+}
+
+// AddAge adds i to age.
+func (uuo *UserUpdateOne) AddAge(i int) *UserUpdateOne {
+	uuo.mutation.AddAge(i)
+	return uuo
+}
+
+// SetName sets the name field.
+func (uuo *UserUpdateOne) SetName(s string) *UserUpdateOne {
+	uuo.mutation.SetName(s)
+	return uuo
+}
+
+// SetNillableName sets the name field if the given value is not nil.
+func (uuo *UserUpdateOne) SetNillableName(s *string) *UserUpdateOne {
+	if s != nil {
+		uuo.SetName(*s)
+	}
+	return uuo
+}
+
+// AddCarIDs adds the cars edge to Car by ids.
+func (uuo *UserUpdateOne) AddCarIDs(ids ...int) *UserUpdateOne {
+	uuo.mutation.AddCarIDs(ids...)
+	return uuo
+}
+
+// AddCars adds the cars edges to Car.
+func (uuo *UserUpdateOne) AddCars(c ...*Car) *UserUpdateOne {
+	ids := make([]int, len(c))
+	for i := range c {
+		ids[i] = c[i].ID
+	}
+	return uuo.AddCarIDs(ids...)
+}
+
+// AddGroupIDs adds the groups edge to Group by ids.
+func (uuo *UserUpdateOne) AddGroupIDs(ids ...int) *UserUpdateOne {
+	uuo.mutation.AddGroupIDs(ids...)
+	return uuo
+}
+
+// AddGroups adds the groups edges to Group.
+func (uuo *UserUpdateOne) AddGroups(g ...*Group) *UserUpdateOne {
+	ids := make([]int, len(g))
+	for i := range g {
+		ids[i] = g[i].ID
+	}
+	return uuo.AddGroupIDs(ids...)
+}
+
+// RemoveCarIDs removes the cars edge to Car by ids.
+func (uuo *UserUpdateOne) RemoveCarIDs(ids ...int) *UserUpdateOne {
+	uuo.mutation.RemoveCarIDs(ids...)
+	return uuo
+}
+
+// RemoveCars removes cars edges to Car.
+func (uuo *UserUpdateOne) RemoveCars(c ...*Car) *UserUpdateOne {
+	ids := make([]int, len(c))
+	for i := range c {
+		ids[i] = c[i].ID
+	}
+	return uuo.RemoveCarIDs(ids...)
+}
+
+// RemoveGroupIDs removes the groups edge to Group by ids.
+func (uuo *UserUpdateOne) RemoveGroupIDs(ids ...int) *UserUpdateOne {
+	uuo.mutation.RemoveGroupIDs(ids...)
+	return uuo
+}
+
+// RemoveGroups removes groups edges to Group.
+func (uuo *UserUpdateOne) RemoveGroups(g ...*Group) *UserUpdateOne {
+	ids := make([]int, len(g))
+	for i := range g {
+		ids[i] = g[i].ID
+	}
+	return uuo.RemoveGroupIDs(ids...)
+}
+
+// Save executes the query and returns the updated entity.
+func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) {
+	if v, ok := uuo.mutation.Age(); ok {
+		if err := user.AgeValidator(v); err != nil {
+			return nil, fmt.Errorf("ent: validator failed for field \"age\": %v", err)
+		}
+	}
+
+	var (
+		err  error
+		node *User
+	)
+	if len(uuo.hooks) == 0 {
+		node, err = uuo.sqlSave(ctx)
+	} else {
+		var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+			mutation, ok := m.(*UserMutation)
+			if !ok {
+				return nil, fmt.Errorf("unexpected mutation type %T", m)
+			}
+			uuo.mutation = mutation
+			node, err = uuo.sqlSave(ctx)
+			return node, err
+		})
+		for i := len(uuo.hooks) - 1; i >= 0; i-- {
+			mut = uuo.hooks[i](mut)
+		}
+		if _, err := mut.Mutate(ctx, uuo.mutation); err != nil {
+			return nil, err
+		}
+	}
+	return node, err
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (uuo *UserUpdateOne) SaveX(ctx context.Context) *User {
+	u, err := uuo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return u
+}
+
+// Exec executes the query on the entity.
+func (uuo *UserUpdateOne) Exec(ctx context.Context) error {
+	_, err := uuo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (uuo *UserUpdateOne) ExecX(ctx context.Context) {
+	if err := uuo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (u *User, err error) {
+	_spec := &sqlgraph.UpdateSpec{
+		Node: &sqlgraph.NodeSpec{
+			Table:   user.Table,
+			Columns: user.Columns,
+			ID: &sqlgraph.FieldSpec{
+				Type:   field.TypeInt,
+				Column: user.FieldID,
+			},
+		},
+	}
+	id, ok := uuo.mutation.ID()
+	if !ok {
+		return nil, fmt.Errorf("missing User.ID for update")
+	}
+	_spec.Node.ID.Value = id
+	if value, ok := uuo.mutation.Age(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt,
+			Value:  value,
+			Column: user.FieldAge,
+		})
+	}
+	if value, ok := uuo.mutation.AddedAge(); ok {
+		_spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{
+			Type:   field.TypeInt,
+			Value:  value,
+			Column: user.FieldAge,
+		})
+	}
+	if value, ok := uuo.mutation.Name(); ok {
+		_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
+			Type:   field.TypeString,
+			Value:  value,
+			Column: user.FieldName,
+		})
+	}
+	if nodes := uuo.mutation.RemovedCarsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   user.CarsTable,
+			Columns: []string{user.CarsColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: car.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := uuo.mutation.CarsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   user.CarsTable,
+			Columns: []string{user.CarsColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: car.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if nodes := uuo.mutation.RemovedGroupsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: true,
+			Table:   user.GroupsTable,
+			Columns: user.GroupsPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: group.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := uuo.mutation.GroupsIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2M,
+			Inverse: true,
+			Table:   user.GroupsTable,
+			Columns: user.GroupsPrimaryKey,
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: &sqlgraph.FieldSpec{
+					Type:   field.TypeInt,
+					Column: group.FieldID,
+				},
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	u = &User{config: uuo.config}
+	_spec.Assign = u.assignValues
+	_spec.ScanValues = u.scanValues()
+	if err = sqlgraph.UpdateNode(ctx, uuo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{user.Label}
+		} else if cerr, ok := isSQLConstraintError(err); ok {
+			err = cerr
+		}
+		return nil, err
+	}
+	return u, nil
+}

+ 8 - 0
go.mod

@@ -0,0 +1,8 @@
+module code.osinet.fr/fgm/entdemo
+
+go 1.14
+
+require (
+	github.com/facebookincubator/ent v0.1.4
+	golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
+)

+ 100 - 0
go.sum

@@ -0,0 +1,100 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/facebookincubator/ent v0.1.4 h1:1Y9icmCryGStA4e8hfNV0VS4wQkoIVZI3tzisus1jTM=
+github.com/facebookincubator/ent v0.1.4/go.mod h1:jiRo17tHbJ3o+lEhwkPtVyavsgEFn38x4BJ6DzXg8wc=
+github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368/go.mod h1:7xCgX1lzlrXPHkfvn3EhumqHkmSlzt8at9q7v0ax19c=
+github.com/go-openapi/inflect v0.18.0 h1:4TMtuIyNxWl29TYpb1grUCuNy+koT0oN5ZXHb6wrZ3E=
+github.com/go-openapi/inflect v0.18.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
+github.com/go-sql-driver/mysql v1.5.1-0.20200311113236-681ffa848bae/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
+github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
+github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
+github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs=
+golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 311 - 0
main.go

@@ -0,0 +1,311 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+	"time"
+
+	"code.osinet.fr/fgm/entdemo/ent"
+	"code.osinet.fr/fgm/entdemo/ent/car"
+	"code.osinet.fr/fgm/entdemo/ent/group"
+	"code.osinet.fr/fgm/entdemo/ent/user"
+
+	_ "github.com/mattn/go-sqlite3"
+)
+
+func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
+	u, err := client.User.
+		Create().
+		SetAge(30).
+		SetName("a8m").
+		Save(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("failed creating user: %w", err)
+	}
+	log.Println("user was created: ", u)
+	return u, nil
+}
+
+func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
+	const name = "a8m"
+	u, err := client.User.
+		Query().
+		Where(user.NameEQ(name)).
+		// "Only" fails if no user found or more than 1 user returned.
+		Only(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("failed querying user %s: %w", name, err)
+	}
+	log.Println("user returned: ", u)
+	return u, nil
+}
+
+func CreateCars(ctx context.Context, client *ent.Client) (*ent.User, error) {
+	var err error
+	name := "tesla"
+	tesla, err := client.Car.
+		Create().
+		SetModel(name).
+		SetRegisteredAt(time.Now()).
+		Save(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("failed creating %s car: %v", name, err)
+	}
+	log.Println("car was created: ", tesla)
+
+	name = "ford"
+	ford, err := client.Car.
+		Create().
+		SetModel(name).
+		SetRegisteredAt(time.Now()).
+		Save(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("failed creating %s car: %v", name, err)
+	}
+	log.Println("car was created: ", ford)
+
+	u, err := QueryUser(ctx, client)
+	if err != nil {
+		return nil, fmt.Errorf("failed looking for user: %w", err)
+	}
+	u.Update().
+		AddCars(tesla, ford).
+		Save(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("failed adding cards to user: %w", err)
+	}
+	return u, nil
+}
+
+func QueryCars(ctx context.Context, user *ent.User) error {
+	cars, err := user.QueryCars().
+		All(ctx)
+	if err != nil {
+		return fmt.Errorf("failed querying user %s cars: %w", user.Name, err)
+	}
+	log.Println("returned all cars: ", cars)
+
+	ford, err := user.QueryCars().
+		Where(car.ModelEQ("ford")).
+		Only(ctx)
+	if err != nil {
+		return fmt.Errorf("failed searching for ford cars: %w", err)
+	}
+	log.Println("filtered cars: ", ford)
+	return nil
+}
+
+func QueryCarUsers(ctx context.Context, u *ent.User) error {
+	cars, err := u.QueryCars().All(ctx)
+	if err != nil {
+		return fmt.Errorf("failed querying user %s cars: %w", u.Name, err)
+	}
+	log.Println("returned all cars: ", cars)
+
+	// query the inverse edge
+	for _, car := range cars {
+		owner, err := car.QueryOwner().Only(ctx)
+		if err != nil {
+			return fmt.Errorf("failed querying cat %s owner: %v", car.Model, err)
+		}
+		log.Printf("card %s owner: %q\n", car.Model, owner.Name)
+	}
+	return nil
+}
+
+func Open() (*ent.Client, error) {
+	const db = "ent.db"
+	err := os.Remove(db)
+	if err != nil {
+		pathErr, ok := err.(*os.PathError)
+		if !ok {
+			log.Fatalf("os.Remove returned a non-PathError: %w", err)
+		}
+		log.Printf("Error removing database: %s", pathErr.Error())
+	}
+	// Running client.Schema.Create will panic is DB exists already.
+	client, err := ent.Open("sqlite3", strings.Join([]string{
+		"file:",
+		db,
+		"?mode=rwc&cache=shared&_fk=1",
+	},
+		"",
+	))
+	// client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
+	return client, err
+}
+
+func BasicDemo(ctx context.Context, client *ent.Client) {
+	u, err := CreateUser(ctx, client)
+	if err != nil {
+		panic(err)
+	}
+	_, err = QueryUser(ctx, client)
+	if err != nil {
+		panic(err)
+	}
+	_, err = CreateCars(ctx, client)
+	if err != nil {
+		panic(err)
+	}
+	err = QueryCars(ctx, u)
+	if err != nil {
+		panic(err)
+	}
+	err = QueryCarUsers(ctx, u)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func CreateGraph(ctx context.Context, client *ent.Client) error {
+	ariel, err := client.User.
+		Create().
+		SetAge(30).
+		SetName("ariel").
+		Save(ctx)
+	if err != nil {
+		return err
+	}
+	neta, err := client.User.
+		Create().
+		SetAge(28).
+		SetName("neta").
+		Save(ctx)
+	if err != nil {
+		return err
+	}
+	// then, create the cars, and attach them to the users in the creation.
+	_, err = client.Car.
+		Create().
+		SetModel("tesla").
+		SetRegisteredAt(time.Now()). // ignore the time in the graph.
+		SetOwner(ariel). // attach this graph to Ariel.
+		Save(ctx)
+	if err != nil {
+		return err
+	}
+	_, err = client.Car.
+		Create().
+		SetModel("mazda").
+		SetRegisteredAt(time.Now()). // ignore the time in the graph.
+		SetOwner(ariel). // attach this graph to Ariel.
+		Save(ctx)
+	if err != nil {
+		return err
+	}
+	_, err = client.Car.
+		Create().
+		SetModel("ford").
+		SetRegisteredAt(time.Now()).
+		SetOwner(neta).
+		Save(ctx)
+	if err != nil {
+		return err
+	}
+
+	// create the groups, and add their users in the creation.
+	_, err = client.Group.
+		Create().
+		SetName("GitLab").
+		AddUsers(neta, ariel).
+		Save(ctx)
+	if err != nil {
+		return err
+	}
+	_, err = client.Group.
+		Create().
+		SetName("GitHub").
+		AddUsers(ariel).
+		Save(ctx)
+	if err != nil {
+		return err
+	}
+	log.Println("The graph was created successfully")
+
+	return nil
+}
+
+func QueryGithub(ctx context.Context, client *ent.Client) error {
+	const name = "GitHub"
+	cars, err := client.Group.
+		Query().
+		Where(group.Name(name)). // (Group(Name=GitHub),)
+		QueryUsers(). // (User(Name=Ariel, Age=30),)
+		QueryCars(). // (Car(Model=Tesla, RegisteredAt=<Time>), Car(Model=Mazda, RegisteredAt=<Time>),)
+		All(ctx)
+	if err != nil {
+		return fmt.Errorf("failed getting %s cars: %w", name, err)
+	}
+	log.Println("cars returned: ", cars)
+	// Output: (Car(Model=tesla, RegisteredAt=<Time>), Car(Model=mazda, RegisteredAt=<Time>),)
+	return nil
+}
+
+// Get the cars of the users of all users in Ariel groups, except for the Mazda.
+func QueryAriel(ctx context.Context, client *ent.Client) error {
+	const name = "ariel"
+	// First get user ariel
+	ariel := client.User.
+		Query().
+		Where(
+			user.HasCars(), // Useless ?
+			user.Name(name),
+		).
+		OnlyX(ctx)
+
+	cars, err := ariel.
+		QueryGroups(). // Get Ariel's groups
+		QueryUsers(). // Get the users in those groups
+		QueryCars(). // Get the cars of the users in those groups
+		Where(
+			car.Not(
+				car.ModelEQ("mazda"),
+			),
+		).
+		All(ctx)
+	if err != nil {
+		return fmt.Errorf("failed getting %s cars: %w", name, err)
+	}
+	log.Println("cars returned: ", cars)
+	// Output: (Car(Model=tesla, RegisteredAt=<Time>), Car(Model=ford, RegisteredAt=<Time>),)
+	return nil
+}
+
+func QueryGroupsWithUsers(ctx context.Context, client *ent.Client) error {
+	groups, err := client.Group.
+		Query().
+		Where(
+			group.HasUsers(),
+		).
+		All(ctx)
+	if err != nil {
+		return fmt.Errorf("failed gettings groups with users: %w", err)
+	}
+	log.Println("groups with users: ", groups)
+	return nil
+}
+
+func main() {
+	client, err := Open()
+	if err != nil {
+		log.Fatalf("failed opening connection to sqlite: %v", err)
+	}
+	defer client.Close()
+
+	// Run the auto-migration tool.
+	if err := client.Schema.Create(context.Background()); err != nil {
+		log.Fatalf("failed creating schema resources: %v", err)
+	}
+
+	bg := context.Background()
+	// BasicDemo(bg, client)
+
+	CreateGraph(bg, client)
+	QueryGithub(bg, client)
+	QueryAriel(bg, client)
+	QueryGroupsWithUsers(bg, client)
+}