Search code examples
pythondjangodjango-ormdjango-postgresql

Django syncdb - How does it know if it previously created the tables or if I did?


I manually created some tables in Postgre for a Django project. I manually created the model too. When I try to syncdb, it throws a database error and says the table already exists.

If syncdb creates the table previously, this won't happen. How does syncdb know whether it created the table or if I created the table?


Solution

  • It seems django maintains an internal cache of apps and their models. It is from here that it knows if it has already created a table for a model or not. I suppose this is why there is support for introspection, so that the models are created from existing schemas and the cache is properly populated.

    From the syncdb source it is clear what the process is, for figuring out what needs to be done on syncdb:

        # Get a list of already installed *models* so that references work right.
        tables = connection.introspection.table_names()
        seen_models = connection.introspection.installed_models(tables)
        created_models = set()
        pending_references = {}
    
        # Build the manifest of apps and models that are to be synchronized
        all_models = [
            (app.__name__.split('.')[-2],
                [m for m in models.get_models(app, include_auto_created=True)
                if router.allow_syncdb(db, m)])
            for app in models.get_apps()
        ]
    
        def model_installed(model):
            opts = model._meta
            converter = connection.introspection.table_name_converter
            return not ((converter(opts.db_table) in tables) or
                (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
    
        manifest = SortedDict(
            (app_name, list(filter(model_installed, model_list)))
            for app_name, model_list in all_models
        )
    

    At each database driver, there is code to fetch the table names. This is for postgresql:

    def get_table_list(self, cursor):
            "Returns a list of table names in the current database."
            cursor.execute("""
                SELECT c.relname
                FROM pg_catalog.pg_class c
                LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
                WHERE c.relkind IN ('r', 'v', '')
                    AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
                    AND pg_catalog.pg_table_is_visible(c.oid)""")
            return [row[0] for row in cursor.fetchall()]