Mysql
 sql >> डेटाबेस >  >> RDS >> Mysql

एक ही विदेशी कुंजी (Django 1.8, MySQL 5.5) वाली वस्तुओं के लिए ऑटोइनक्रिकमेंट-जैसे फ़ील्ड

मैंने एक डेटाबेस लेनदेन में ऑटोइनक्रिकमेंट और सम्मिलन करने के लिए कच्ची SQL क्वेरी का निर्माण और निष्पादन करके समस्या को हल करना समाप्त कर दिया। मैंने Django स्रोत कोड के माध्यम से यह समझने के लिए बहुत समय बिताया कि उनका डिफ़ॉल्ट मॉडल सहेजने का तरीका कैसे काम करता है ताकि मैं इसे यथासंभव मजबूती से कर सकूं। हालांकि, मुझे पूरी उम्मीद है कि गैर-MySQL बैकएंड के लिए इसे संशोधित करने की आवश्यकता होगी।

सबसे पहले, मैंने एक अमूर्त वर्ग बनाया जो अब ऑब्जेक्टलॉग से प्राप्त होगा, जो इस नई बचत विधि को पेश करता है:

class AutoIncrementModel(models.Model):
    """
    An abstract class used as a base for classes which need the
    autoincrementing save method described below.
    """
    class Meta:
        abstract = True

    def save(self, auto_field, auto_fk, *args, **kwargs):
        """
        Arguments:
            auto_field: name of field which acts as an autoincrement field.
            auto_fk:    name of ForeignKey to which the auto_field is relative.
        """

        # Do normal save if this is not an insert (i.e., the instance has a
        # primary key already).
        meta = self.__class__._meta
        pk_set = self._get_pk_val(meta) is not None
        if pk_set:
            super(ObjectLog, self).save(*args, **kwargs)
            return

        # Otherwise, we'll generate some raw SQL to do the
        # insert and auto-increment.

        # Get model fields, except for primary key field.
        fields = meta.local_concrete_fields
        if not pk_set:
            fields = [f for f in fields if not
                isinstance(f, models.fields.AutoField)]

        # Setup for generating base SQL query for doing an INSERT.
        query = models.sql.InsertQuery(self.__class__._base_manager.model)
        query.insert_values(fields, objs=[self])
        compiler = query.get_compiler(using=self.__class__._base_manager.db)
        compiler.return_id = meta.has_auto_field and not pk_set

        fk_name = meta.get_field(auto_fk).column
        with compiler.connection.cursor() as cursor:
            # Get base SQL query as string.
            for sql, params in compiler.as_sql():
                # compiler.as_sql() looks like:
                # INSERT INTO `table_objectlog` VALUES (%s,...,%s)
                # We modify this to do:
                # INSERT INTO `table_objectlog` SELECT %s,...,%s FROM
                # `table_objectlog` WHERE `object_id`=id
                # NOTE: it's unlikely that the following will generate
                # a functional database query for non-MySQL backends.

                # Replace VALUES (%s, %s, ..., %s) with
                # SELECT %s, %s, ..., %s
                sql = re.sub(r"VALUES \((.*)\)", r"SELECT \1", sql)

                # Add table to SELECT from and ForeignKey id corresponding to
                # our autoincrement field.
                sql += " FROM `{tbl_name}` WHERE `{fk_name}`={fk_id}".format(
                    tbl_name=meta.db_table,
                    fk_name=fk_name,
                    fk_id=getattr(self, fk_name)
                    )

                # Get index corresponding to auto_field.
                af_idx = [f.name for f in fields].index(auto_field)
                # Put this directly in the SQL. If we use parameter
                # substitution with cursor.execute, it gets quoted
                # as a literal, which causes the SQL command to fail.
                # We shouldn't have issues with SQL injection because
                # auto_field should never be a user-defined parameter.
                del params[af_idx]
                sql = re.sub(r"((%s, ){{{0}}})%s".format(af_idx),
                r"\1IFNULL(MAX({af}),0)+1", sql, 1).format(af=auto_field)

                # IFNULL(MAX({af}),0)+1 is the autoincrement SQL command,
                # {af} is substituted as the column name.

                # Execute SQL command.
                cursor.execute(sql, params)

            # Get primary key from database and set it in memory.
            if compiler.connection.features.can_return_id_from_insert:
                id = compiler.connection.ops.fetch_returned_insert_id(cursor)
            else:
                id = compiler.connection.ops.last_insert_id(cursor,
                    meta.db_table, meta.pk.column)
            self._set_pk_val(id)

            # Refresh object in memory in order to get auto_field value.
            self.refresh_from_db()

तब ऑब्जेक्टलॉग मॉडल इस तरह उपयोग करता है:

class ObjectLog(AutoIncrementModel):
    class Meta:
        ordering = ['-created','-N']
        unique_together = ("object","N")
    object = models.ForeignKey(Object, null=False)                                                                                                                                                              
    created = models.DateTimeField(auto_now_add=True)
    issuer = models.ForeignKey(User)
    N = models.IntegerField(null=False)

    def save(self, *args, **kwargs):
        # Set up to call save method of the base class (AutoIncrementModel)
        kwargs.update({'auto_field': 'N', 'auto_fk': 'event'})
        super(EventLog, self).save(*args, **kwargs)

यह ObjectLog.save() को कॉल को अभी भी अपेक्षित रूप से काम करने की अनुमति देता है।




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. php mysql अद्यतन आदेश अद्यतन नहीं होगा

  2. मेरे पहले MySQL प्रोजेक्ट की योजना बनाने के लिए

  3. वर्षों, महीनों और दिनों में आयु की गणना करें

  4. MySQL के लिए Percona सर्वर की निगरानी - प्रमुख मेट्रिक्स

  5. MySQL में अनेक:अनेक संबंध कोडिंग पर कुछ सलाह और प्रतिक्रिया की आवश्यकता है