Best Python code snippet using autotest_python
models.py
Source:models.py  
1from django.db import models as dbmodels, connection2from autotest_lib.frontend.afe import model_logic, readonly_connection3_quote_name = connection.ops.quote_name4class TempManager(model_logic.ExtendedManager):5    """A Temp Manager."""6    _GROUP_COUNT_NAME = 'group_count'7    def _get_key_unless_is_function(self, field):8        if '(' in field:9            return field10        return self.get_key_on_this_table(field)11    def _get_field_names(self, fields, extra_select_fields={}):12        field_names = []13        for field in fields:14            if field in extra_select_fields:15                field_names.append(extra_select_fields[field][0])16            else:17                field_names.append(self._get_key_unless_is_function(field))18        return field_names19    def _get_group_query_sql(self, query, group_by):20        compiler = query.query.get_compiler(using=query.db)21        sql, params = compiler.as_sql()22        # insert GROUP BY clause into query23        group_fields = self._get_field_names(group_by, query.query.extra_select)24        group_by_clause = ' GROUP BY ' + ', '.join(group_fields)25        group_by_position = sql.rfind('ORDER BY')26        if group_by_position == -1:27            group_by_position = len(sql)28        sql = (sql[:group_by_position] +29               group_by_clause + ' ' +30               sql[group_by_position:])31        return sql, params32    def _get_column_names(self, cursor):33        """Gets the column names from the cursor description.34        This method exists so that it can be mocked in the unit test for35        sqlite3 compatibility.36        """37        return [column_info[0] for column_info in cursor.description]38    def execute_group_query(self, query, group_by):39        """Performs the given query grouped by the specified fields.40        The given query's extra select fields are added.41        @param query: The query to perform.42        @param group_by: The fields by which to group.43        @return A list of dicts, where each dict corresponds to single row and44            contains a key for each grouped field as well as all of the extra45            select fields.46        """47        sql, params = self._get_group_query_sql(query, group_by)48        cursor = readonly_connection.cursor()49        cursor.execute(sql, params)50        field_names = self._get_column_names(cursor)51        row_dicts = [dict(zip(field_names, row)) for row in cursor.fetchall()]52        return row_dicts53    def get_count_sql(self, query):54        """Get SQL to select a per-group count of unique matches for a query.55        @param query: The query to use.56        @return A tuple (field alias, field SQL).57        """58        if query.query.distinct:59            pk_field = self.get_key_on_this_table()60            count_sql = 'COUNT(DISTINCT %s)' % pk_field61        else:62            count_sql = 'COUNT(1)'63        return self._GROUP_COUNT_NAME, count_sql64    def _get_num_groups_sql(self, query, group_by):65        group_fields = self._get_field_names(group_by, query.query.extra_select)66        query = query.order_by() # this can mess up the query and isn't needed67        compiler = query.query.get_compiler(using=query.db)68        sql, params = compiler.as_sql()69        from_ = sql[sql.find(' FROM'):]70        return ('SELECT DISTINCT %s %s' % (','.join(group_fields),71                                                  from_),72                params)73    def _cursor_rowcount(self, cursor):74        """To be stubbed by tests"""75        return cursor.rowcount76    def get_num_groups(self, query, group_by):77        """Gets the number of distinct groups for a query.78        @param query: The query to use.79        @param group_by: The fields by which to group.80        @return The number of distinct groups for the given query grouped by81            the fields in group_by.82        """83        sql, params = self._get_num_groups_sql(query, group_by)84        cursor = readonly_connection.cursor()85        cursor.execute(sql, params)86        return self._cursor_rowcount(cursor)87class Machine(dbmodels.Model):88    """Models a machine."""89    machine_idx = dbmodels.AutoField(primary_key=True)90    hostname = dbmodels.CharField(unique=True, max_length=255)91    machine_group = dbmodels.CharField(blank=True, max_length=240)92    owner = dbmodels.CharField(blank=True, max_length=240)93    class Meta:94        """Metadata for class Machine."""95        db_table = 'tko_machines'96class Kernel(dbmodels.Model):97    """Models a kernel."""98    kernel_idx = dbmodels.AutoField(primary_key=True)99    kernel_hash = dbmodels.CharField(max_length=105, editable=False)100    base = dbmodels.CharField(max_length=90)101    printable = dbmodels.CharField(max_length=300)102    class Meta:103        """Metadata for class Kernel."""104        db_table = 'tko_kernels'105class Patch(dbmodels.Model):106    """Models a patch."""107    kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')108    name = dbmodels.CharField(blank=True, max_length=240)109    url = dbmodels.CharField(blank=True, max_length=900)110    the_hash = dbmodels.CharField(blank=True, max_length=105, db_column='hash')111    class Meta:112        """Metadata for class Patch."""113        db_table = 'tko_patches'114class Status(dbmodels.Model):115    """Models a status."""116    status_idx = dbmodels.AutoField(primary_key=True)117    word = dbmodels.CharField(max_length=30)118    class Meta:119        """Metadata for class Status."""120        db_table = 'tko_status'121class Job(dbmodels.Model, model_logic.ModelExtensions):122    """Models a job."""123    job_idx = dbmodels.AutoField(primary_key=True)124    tag = dbmodels.CharField(unique=True, max_length=100)125    label = dbmodels.CharField(max_length=300)126    username = dbmodels.CharField(max_length=240)127    machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')128    queued_time = dbmodels.DateTimeField(null=True, blank=True)129    started_time = dbmodels.DateTimeField(null=True, blank=True)130    finished_time = dbmodels.DateTimeField(null=True, blank=True)131    afe_job_id = dbmodels.IntegerField(null=True, default=None)132    objects = model_logic.ExtendedManager()133    class Meta:134        """Metadata for class Job."""135        db_table = 'tko_jobs'136class JobKeyval(dbmodels.Model):137    """Models a job keyval."""138    job = dbmodels.ForeignKey(Job)139    key = dbmodels.CharField(max_length=90)140    value = dbmodels.CharField(blank=True, max_length=300)141    class Meta:142        """Metadata for class JobKeyval."""143        db_table = 'tko_job_keyvals'144class Test(dbmodels.Model, model_logic.ModelExtensions,145           model_logic.ModelWithAttributes):146    """Models a test."""147    test_idx = dbmodels.AutoField(primary_key=True)148    job = dbmodels.ForeignKey(Job, db_column='job_idx')149    test = dbmodels.CharField(max_length=300)150    subdir = dbmodels.CharField(blank=True, max_length=300)151    kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')152    status = dbmodels.ForeignKey(Status, db_column='status')153    reason = dbmodels.CharField(blank=True, max_length=3072)154    machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')155    finished_time = dbmodels.DateTimeField(null=True, blank=True)156    started_time = dbmodels.DateTimeField(null=True, blank=True)157    invalid = dbmodels.BooleanField(default=False)158    invalidates_test = dbmodels.ForeignKey(159            'self', null=True, db_column='invalidates_test_idx',160            related_name='invalidates_test_set')161    objects = model_logic.ExtendedManager()162    def _get_attribute_model_and_args(self, attribute):163        return TestAttribute, dict(test=self, attribute=attribute,164                                   user_created=True)165    def set_attribute(self, attribute, value):166        # ensure non-user-created attributes remain immutable167        try:168            TestAttribute.objects.get(test=self, attribute=attribute,169                                      user_created=False)170            raise ValueError('Attribute %s already exists for test %s and is '171                             'immutable' % (attribute, self.test_idx))172        except TestAttribute.DoesNotExist:173            super(Test, self).set_attribute(attribute, value)174    class Meta:175        """Metadata for class Test."""176        db_table = 'tko_tests'177class TestAttribute(dbmodels.Model, model_logic.ModelExtensions):178    """Models a test attribute."""179    test = dbmodels.ForeignKey(Test, db_column='test_idx')180    attribute = dbmodels.CharField(max_length=90)181    value = dbmodels.CharField(blank=True, max_length=300)182    user_created = dbmodels.BooleanField(default=False)183    objects = model_logic.ExtendedManager()184    class Meta:185        """Metadata for class TestAttribute."""186        db_table = 'tko_test_attributes'187class IterationAttribute(dbmodels.Model, model_logic.ModelExtensions):188    """Models an iteration attribute."""189    # This isn't really a primary key, but it's necessary to appease Django190    # and is harmless as long as we're careful.191    test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)192    iteration = dbmodels.IntegerField()193    attribute = dbmodels.CharField(max_length=90)194    value = dbmodels.CharField(blank=True, max_length=300)195    objects = model_logic.ExtendedManager()196    class Meta:197        """Metadata for class IterationAttribute."""198        db_table = 'tko_iteration_attributes'199class IterationResult(dbmodels.Model, model_logic.ModelExtensions):200    """Models an iteration result."""201    # See comment on IterationAttribute regarding primary_key=True.202    test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)203    iteration = dbmodels.IntegerField()204    attribute = dbmodels.CharField(max_length=256)205    value = dbmodels.FloatField(null=True, blank=True)206    objects = model_logic.ExtendedManager()207    class Meta:208        """Metadata for class IterationResult."""209        db_table = 'tko_iteration_result'210class TestLabel(dbmodels.Model, model_logic.ModelExtensions):211    """Models a test label."""212    name = dbmodels.CharField(max_length=80, unique=True)213    description = dbmodels.TextField(blank=True)214    tests = dbmodels.ManyToManyField(Test, blank=True,215                                     db_table='tko_test_labels_tests')216    name_field = 'name'217    objects = model_logic.ExtendedManager()218    class Meta:219        """Metadata for class TestLabel."""220        db_table = 'tko_test_labels'221class SavedQuery(dbmodels.Model, model_logic.ModelExtensions):222    """Models a saved query."""223    # TODO: change this to foreign key once DBs are merged.224    owner = dbmodels.CharField(max_length=80)225    name = dbmodels.CharField(max_length=100)226    url_token = dbmodels.TextField()227    class Meta:228        """Metadata for class SavedQuery."""229        db_table = 'tko_saved_queries'230# Views.231class TestViewManager(TempManager):232    """A Test View Manager."""233    def get_query_set(self):234        query = super(TestViewManager, self).get_query_set()235        # add extra fields to selects, using the SQL itself as the "alias"236        extra_select = dict((sql, sql)237                            for sql in self.model.extra_fields.iterkeys())238        return query.extra(select=extra_select)239    def _get_include_exclude_suffix(self, exclude):240        if exclude:241            return '_exclude'242        return '_include'243    def _add_attribute_join(self, query_set, join_condition,244                            suffix=None, exclude=False):245        if suffix is None:246            suffix = self._get_include_exclude_suffix(exclude)247        return self.add_join(query_set, 'tko_test_attributes',248                             join_key='test_idx',249                             join_condition=join_condition,250                             suffix=suffix, exclude=exclude)251    def _add_label_pivot_table_join(self, query_set, suffix, join_condition='',252                                    exclude=False, force_left_join=False):253        return self.add_join(query_set, 'tko_test_labels_tests',254                             join_key='test_id',255                             join_condition=join_condition,256                             suffix=suffix, exclude=exclude,257                             force_left_join=force_left_join)258    def _add_label_joins(self, query_set, suffix=''):259        query_set = self._add_label_pivot_table_join(260                query_set, suffix=suffix, force_left_join=True)261        # since we're not joining from the original table, we can't use262        # self.add_join() again263        second_join_alias = 'tko_test_labels' + suffix264        second_join_condition = ('%s.id = %s.testlabel_id' %265                                 (second_join_alias,266                                  'tko_test_labels_tests' + suffix))267        query_set.query.add_custom_join('tko_test_labels',268                                        second_join_condition,269                                        query_set.query.LOUTER,270                                        alias=second_join_alias)271        return query_set272    def _get_label_ids_from_names(self, label_names):273        label_ids = list( # listifying avoids a double query below274                TestLabel.objects.filter(name__in=label_names)275                .values_list('name', 'id'))276        if len(label_ids) < len(set(label_names)):277            raise ValueError('Not all labels found: %s' %278                             ', '.join(label_names))279        return dict(name_and_id for name_and_id in label_ids)280    def _include_or_exclude_labels(self, query_set, label_names, exclude=False):281        label_ids = self._get_label_ids_from_names(label_names).itervalues()282        suffix = self._get_include_exclude_suffix(exclude)283        condition = ('tko_test_labels_tests%s.testlabel_id IN (%s)' %284                     (suffix,285                      ','.join(str(label_id) for label_id in label_ids)))286        return self._add_label_pivot_table_join(query_set,287                                                join_condition=condition,288                                                suffix=suffix,289                                                exclude=exclude)290    def _add_custom_select(self, query_set, select_name, select_sql):291        return query_set.extra(select={select_name: select_sql})292    def _add_select_value(self, query_set, alias):293        return self._add_custom_select(query_set, alias,294                                       _quote_name(alias) + '.value')295    def _add_select_ifnull(self, query_set, alias, non_null_value):296        select_sql = "IF(%s.id IS NOT NULL, '%s', NULL)" % (_quote_name(alias),297                                                            non_null_value)298        return self._add_custom_select(query_set, alias, select_sql)299    def _join_test_label_column(self, query_set, label_name, label_id):300        alias = 'test_label_' + label_name301        label_query = TestLabel.objects.filter(name=label_name)302        query_set = Test.objects.join_custom_field(query_set, label_query,303                                                   alias)304        query_set = self._add_select_ifnull(query_set, alias, label_name)305        return query_set306    def _join_test_label_columns(self, query_set, label_names):307        label_id_map = self._get_label_ids_from_names(label_names)308        for label_name in label_names:309            query_set = self._join_test_label_column(query_set, label_name,310                                                     label_id_map[label_name])311        return query_set312    def _join_test_attribute(self, query_set, attribute, alias=None,313                             extra_join_condition=None):314        """315        Join the given TestView QuerySet to TestAttribute.  The resulting query316        has an additional column for the given attribute named317        "attribute_<attribute name>".318        """319        if not alias:320            alias = 'test_attribute_' + attribute321        attribute_query = TestAttribute.objects.filter(attribute=attribute)322        if extra_join_condition:323            attribute_query = attribute_query.extra(324                    where=[extra_join_condition])325        query_set = Test.objects.join_custom_field(query_set, attribute_query,326                                                   alias)327        query_set = self._add_select_value(query_set, alias)328        return query_set329    def _join_machine_label_columns(self, query_set, machine_label_names):330        for label_name in machine_label_names:331            alias = 'machine_label_' + label_name332            condition = "FIND_IN_SET('%s', %s)" % (333                    label_name, _quote_name(alias) + '.value')334            query_set = self._join_test_attribute(335                    query_set, 'host-labels',336                    alias=alias, extra_join_condition=condition)337            query_set = self._add_select_ifnull(query_set, alias, label_name)338        return query_set339    def _join_one_iteration_key(self, query_set, result_key, first_alias=None):340        alias = 'iteration_result_' + result_key341        iteration_query = IterationResult.objects.filter(attribute=result_key)342        if first_alias:343            # after the first join, we need to match up iteration indices,344            # otherwise each join will expand the query by the number of345            # iterations and we'll have extraneous rows346            iteration_query = iteration_query.extra(347                    where=['%s.iteration = %s.iteration'348                           % (_quote_name(alias), _quote_name(first_alias))])349        query_set = Test.objects.join_custom_field(query_set, iteration_query,350                                                   alias, left_join=False)351        # select the iteration value and index for this join352        query_set = self._add_select_value(query_set, alias)353        if not first_alias:354            # for first join, add iteration index select too355            query_set = self._add_custom_select(356                    query_set, 'iteration_index',357                    _quote_name(alias) + '.iteration')358        return query_set, alias359    def _join_iteration_results(self, test_view_query_set, result_keys):360        """Join the given TestView QuerySet to IterationResult for one result.361        The resulting query looks like a TestView query but has one row per362        iteration.  Each row includes all the attributes of TestView, an363        attribute for each key in result_keys and an iteration_index attribute.364        We accomplish this by joining the TestView query to IterationResult365        once per result key.  Each join is restricted on the result key (and on366        the test index, like all one-to-many joins).  For the first join, this367        is the only restriction, so each TestView row expands to a row per368        iteration (per iteration that includes the key, of course).  For each369        subsequent join, we also restrict the iteration index to match that of370        the initial join.  This makes each subsequent join produce exactly one371        result row for each input row.  (This assumes each iteration contains372        the same set of keys.  Results are undefined if that's not true.)373        """374        if not result_keys:375            return test_view_query_set376        query_set, first_alias = self._join_one_iteration_key(377                test_view_query_set, result_keys[0])378        for result_key in result_keys[1:]:379            query_set, _ = self._join_one_iteration_key(query_set, result_key,380                                                        first_alias=first_alias)381        return query_set382    def _join_job_keyvals(self, query_set, job_keyvals):383        for job_keyval in job_keyvals:384            alias = 'job_keyval_' + job_keyval385            keyval_query = JobKeyval.objects.filter(key=job_keyval)386            query_set = Job.objects.join_custom_field(query_set, keyval_query,387                                                       alias)388            query_set = self._add_select_value(query_set, alias)389        return query_set390    def _join_iteration_attributes(self, query_set, iteration_attributes):391        for attribute in iteration_attributes:392            alias = 'iteration_attribute_' + attribute393            attribute_query = IterationAttribute.objects.filter(394                    attribute=attribute)395            query_set = Test.objects.join_custom_field(query_set,396                                                       attribute_query, alias)397            query_set = self._add_select_value(query_set, alias)398        return query_set399    def get_query_set_with_joins(self, filter_data):400        """Add joins for querying over test-related items.401        These parameters are supported going forward:402        * test_attribute_fields: list of attribute names.  Each attribute will403                be available as a column attribute_<name>.value.404        * test_label_fields: list of label names.  Each label will be available405                as a column label_<name>.id, non-null iff the label is present.406        * iteration_result_fields: list of iteration result names.  Each407                result will be available as a column iteration_<name>.value.408                Note that this changes the semantics to return iterations409                instead of tests -- if a test has multiple iterations, a row410                will be returned for each one.  The iteration index is also411                available as iteration_<name>.iteration.412        * machine_label_fields: list of machine label names.  Each will be413                available as a column machine_label_<name>.id, non-null iff the414                label is present on the machine used in the test.415        * job_keyval_fields: list of job keyval names. Each value will be416                available as a column job_keyval_<name>.id, non-null iff the417                keyval is present in the AFE job.418        * iteration_attribute_fields: list of iteration attribute names. Each419                attribute will be available as a column420                iteration_attribute<name>.id, non-null iff the attribute is421                present.422        These parameters are deprecated:423        * include_labels424        * exclude_labels425        * include_attributes_where426        * exclude_attributes_where427        Additionally, this method adds joins if the following strings are428        present in extra_where (this is also deprecated):429        * test_labels430        * test_attributes_host_labels431        @param filter_data: Data by which to filter.432        @return A QuerySet.433        """434        query_set = self.get_query_set()435        test_attributes = filter_data.pop('test_attribute_fields', [])436        for attribute in test_attributes:437            query_set = self._join_test_attribute(query_set, attribute)438        test_labels = filter_data.pop('test_label_fields', [])439        query_set = self._join_test_label_columns(query_set, test_labels)440        machine_labels = filter_data.pop('machine_label_fields', [])441        query_set = self._join_machine_label_columns(query_set, machine_labels)442        iteration_keys = filter_data.pop('iteration_result_fields', [])443        query_set = self._join_iteration_results(query_set, iteration_keys)444        job_keyvals = filter_data.pop('job_keyval_fields', [])445        query_set = self._join_job_keyvals(query_set, job_keyvals)446        iteration_attributes = filter_data.pop('iteration_attribute_fields', [])447        query_set = self._join_iteration_attributes(query_set,448                                                    iteration_attributes)449        # everything that follows is deprecated behavior450        joined = False451        extra_where = filter_data.get('extra_where', '')452        if 'tko_test_labels' in extra_where:453            query_set = self._add_label_joins(query_set)454            joined = True455        include_labels = filter_data.pop('include_labels', [])456        exclude_labels = filter_data.pop('exclude_labels', [])457        if include_labels:458            query_set = self._include_or_exclude_labels(query_set,459                                                        include_labels)460            joined = True461        if exclude_labels:462            query_set = self._include_or_exclude_labels(query_set,463                                                        exclude_labels,464                                                        exclude=True)465            joined = True466        include_attributes_where = filter_data.pop('include_attributes_where',467                                                   '')468        exclude_attributes_where = filter_data.pop('exclude_attributes_where',469                                                   '')470        if include_attributes_where:471            query_set = self._add_attribute_join(472                query_set,473                join_condition=self.escape_user_sql(include_attributes_where))474            joined = True475        if exclude_attributes_where:476            query_set = self._add_attribute_join(477                query_set,478                join_condition=self.escape_user_sql(exclude_attributes_where),479                exclude=True)480            joined = True481        if not joined:482            filter_data['no_distinct'] = True483        if 'tko_test_attributes_host_labels' in extra_where:484            query_set = self._add_attribute_join(485                query_set, suffix='_host_labels',486                join_condition='tko_test_attributes_host_labels.attribute = '487                               '"host-labels"')488        return query_set489    def query_test_ids(self, filter_data, apply_presentation=True):490        """Queries for test IDs.491        @param filter_data: Data by which to filter.492        @param apply_presentation: Whether or not to apply presentation493            parameters.494        @return A list of test IDs.495        """496        query = self.model.query_objects(filter_data,497                                         apply_presentation=apply_presentation)498        dicts = query.values('test_idx')499        return [item['test_idx'] for item in dicts]500    def query_test_label_ids(self, filter_data):501        """Queries for test label IDs.502        @param filter_data: Data by which to filter.503        @return A list of test label IDs.504        """505        query_set = self.model.query_objects(filter_data)506        query_set = self._add_label_joins(query_set, suffix='_list')507        rows = self._custom_select_query(query_set, ['tko_test_labels_list.id'])508        return [row[0] for row in rows if row[0] is not None]509    def escape_user_sql(self, sql):510        sql = super(TestViewManager, self).escape_user_sql(sql)511        return sql.replace('test_idx', self.get_key_on_this_table('test_idx'))512class TestView(dbmodels.Model, model_logic.ModelExtensions):513    """Models a test view."""514    extra_fields = {515            'DATE(job_queued_time)': 'job queued day',516            'DATE(test_finished_time)': 'test finished day',517    }518    group_fields = [519            'test_name',520            'status',521            'kernel',522            'hostname',523            'job_tag',524            'job_name',525            'platform',526            'reason',527            'job_owner',528            'job_queued_time',529            'DATE(job_queued_time)',530            'test_started_time',531            'test_finished_time',532            'DATE(test_finished_time)',533    ]534    test_idx = dbmodels.IntegerField('test index', primary_key=True)535    job_idx = dbmodels.IntegerField('job index', null=True, blank=True)536    test_name = dbmodels.CharField(blank=True, max_length=90)537    subdir = dbmodels.CharField('subdirectory', blank=True, max_length=180)538    kernel_idx = dbmodels.IntegerField('kernel index')539    status_idx = dbmodels.IntegerField('status index')540    reason = dbmodels.CharField(blank=True, max_length=3072)541    machine_idx = dbmodels.IntegerField('host index')542    test_started_time = dbmodels.DateTimeField(null=True, blank=True)543    test_finished_time = dbmodels.DateTimeField(null=True, blank=True)544    job_tag = dbmodels.CharField(blank=True, max_length=300)545    job_name = dbmodels.CharField(blank=True, max_length=300)546    job_owner = dbmodels.CharField('owner', blank=True, max_length=240)547    job_queued_time = dbmodels.DateTimeField(null=True, blank=True)548    job_started_time = dbmodels.DateTimeField(null=True, blank=True)549    job_finished_time = dbmodels.DateTimeField(null=True, blank=True)550    afe_job_id = dbmodels.IntegerField(null=True)551    hostname = dbmodels.CharField(blank=True, max_length=300)552    platform = dbmodels.CharField(blank=True, max_length=240)553    machine_owner = dbmodels.CharField(blank=True, max_length=240)554    kernel_hash = dbmodels.CharField(blank=True, max_length=105)555    kernel_base = dbmodels.CharField(blank=True, max_length=90)556    kernel = dbmodels.CharField(blank=True, max_length=300)557    status = dbmodels.CharField(blank=True, max_length=30)558    invalid = dbmodels.BooleanField(blank=True)559    invalidates_test_idx = dbmodels.IntegerField(null=True, blank=True)560    objects = TestViewManager()561    def save(self):562        raise NotImplementedError('TestView is read-only')563    def delete(self):564        raise NotImplementedError('TestView is read-only')565    @classmethod566    def query_objects(cls, filter_data, initial_query=None,567                      apply_presentation=True):568        if initial_query is None:569            initial_query = cls.objects.get_query_set_with_joins(filter_data)570        return super(TestView, cls).query_objects(571                filter_data, initial_query=initial_query,572                apply_presentation=apply_presentation)573    class Meta:574        """Metadata for class TestView."""...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
