def add_job(cls, identifier, queue_name=None, priority=0, queue_model=None,
                prepend=False, delayed_for=None, delayed_until=None,
                **fields_if_new):
        """
        Add a job to a queue.
        If this job already exists, check it's current priority. If its higher
        than the new one, don't touch it, else move the job to the wanted queue.
        Before setting/moving the job to the queue, check for a `delayed_for`
        (int/foat/timedelta) or `delayed_until` (datetime) argument to see if
        it must be delayed instead of queued.
        If the job is created, fields in fields_if_new will be set for the new
        job.
        Finally return the job.
        """

        # check for delayed_for/delayed_until arguments
        delayed_until = compute_delayed_until(delayed_for, delayed_until)

        # create the job or get an existing one
        job_kwargs = {'identifier': identifier, 'queued': '1'}
        retries = 0
        while retries < 10:
            retries += 1
            try:
                job, created = cls.get_or_connect(**job_kwargs)
            except IndexError:
                # Failure during the retrieval https://friendpaste.com/5U63a8aFuV44SEgQckgMP
                # => retry
                continue
            except ValueError:
                # more than one already in the queue !
                try:
                    job = cls.collection(**job_kwargs).instances()[0]
                except IndexError:
                    # but no more now ?!
                    # => retry
                    continue
                else:
                    created = False

            # ok we have our job, stop now
            break

        try:
            # check queue_name
            queue_name = cls._get_queue_name(queue_name)

            # if the job already exists, and we want a higher priority or move it,
            # start by updating it
            if not created:
                current_priority = int(job.priority.hget() or 0)
                # if the job has a higher priority, or don't need to be moved,
                # don't move it
                if not prepend and current_priority >= priority:
                    return job

                # cancel it temporarily, we'll set it as waiting later
                job.status.hset(STATUSES.CANCELED)

                # remove it from the current queue, we'll add it to the new one later
                if queue_model is None:
                    queue_model = cls.queue_model
                current_queue = queue_model.get_queue(queue_name, current_priority)
                current_queue.waiting.lrem(0, job.ident)

            else:
                job.set_fields(added=str(datetime.utcnow()), **(fields_if_new or {}))

            # add the job to the queue
            job.enqueue_or_delay(queue_name, priority, delayed_until, prepend, queue_model)

            return job
        except Exception:
            job.queued.delete()
            raise